2 Commits
Author SHA1 Message Date
DuvelCorp 233899b2a0 v1.0.6 2026-03-05 12:30:24 +01:00
DuvelCorp 1a28d95b87 v1.0.5 2026-03-01 09:10:22 +01:00
41 changed files with 2453 additions and 413 deletions
-1
View File
@@ -9,4 +9,3 @@
tools/ tools/
LOC*.md LOC*.md
LOCALIZATION*.md LOCALIZATION*.md
CHANGELOG.md
+70
View File
@@ -0,0 +1,70 @@
# Changelog
All notable changes to MetaHunt will be documented in this file.
## [1.0.6] - 2026-03-05
### Fixed
- Added missing abilities to all pet families.
- Rescraped from Twow DB the previously in correct `Roar of Fortitude` pet-ability.
- Fixed an issue with Tooltip on pet action bar abilities, that were falsely displayed as not learned yet for rankless abilities.
- Restored visible Stable Master auto-scan feedback (`Stable scan complete: X slot(s).`) when opening the stable window.
- Fixed stable scan icon persistence for active pets that were never stabled by capturing current-pet icon data during stable scans.
- Hardened stable-scan bootstrap to initialize on both `PET_STABLE_SHOW` and `PET_STABLE_UPDATE` event paths.
- Fixed issues with taming pet metadata that were not properly recorded since the last update.
- Fixed issues with pet runaway interception that was broken since the last update.
- Updated version-update notification text to include clearer upgrade guidance and plain GitHub URL text for updates.
- Fixed an issue with `zTrack` buttons spells being incorrect after having respec the talents using brainwashing device.
## [1.0.5] - 2026-03-01
### Fixed
- Multiple changes to the addon to reduce its memory and CPU time footprint to the strict minimum possible.
- Restored FeedOMatic pet hunger notifications so hungry/very hungry warnings are emitted reliably again when enabled.
- Fixed Chronometer `Wing Clip` tracking by adding a safe fallback timer path for the missing trigger case.
- Fixed Chronometer `Quick Shots` bar icon by mapping the event to the correct icon texture.
- Fixed module enabled/disabled persistence to be truly per-character (`MTH_CharSavedVariables.moduleStates`) instead of account-shared.
- Fixed AutoBuy persistence fallback by removing undeclared `MTH_AutoBuy_Saved` paths and using module stores/transient runtime fallback only.
- Consolidated FeedOMatic legacy persistence to a single module-backed store (`modules.feedomatic.legacy`) with globals bound to that source.
- Switched Chronometer profile persistence to per-character module storage with one-time migration from existing account profile data.
- Switched Tooltips module options persistence to per-character module storage with one-time migration from existing account settings.
## [1.0.4] - 2026-02-27
### Added
- Added new zButton bar `zRanged` that tracks all ranged weapons in bags and allows to quickly swap them.
- Added structured localization architecture with dedicated locale packs under `locales/` and loader wiring through `init/localization.xml`.
- Added a dedicated `Messages` options panel with per-message toggles.
- Added Smart Ammo option `Enable Weapon-Swap Auto Ammo` to instantly equip best bullets/arrows when switching ranged weapon type.
- Added two new Hunter Book pages in the main tab bar: `Projectiles` and `Ammo Bags`
### Changed
- Reworked core localization runtime to support explicit locale fallback rules (`enUS`, `deDE`, `esES`, `ptBR`, `ruRU`, `zhCN`; no `frFR` target).
- Updated Hunter Book NPC finder and map vendor markers to use localized NPC names by NPC ID.
- Updated Hunter Book, item search/sort, AutoBuy item labels, and drop map markers to use localized item names by item ID.
### Fixed
- Fixed version metadata consistency so all first-party module descriptors report the same release version as the main addon.
- Fixed SmartAmmo junk-shot detection by including `Baited Shot` and `Tranquilizing Shot` in `MTH_AMMO_JUNKSHOT_SET`.
- Fixed zButtons options side effect where opening Mounts/Companions/Toys options could implicitly enable those buttons.
- Normalized zButtons checkbox defaults (`enabled`, `tooltip`, `hideonclick`, `parent.hide`, `parent.circle`, `showammoname`) so toggle states are always explicit and never nil.
- Fixed Beast Lore ability dropdown to exclude trainer-only abilities and list beast-learned abilities only.
- Removed junk `TBD` ability value from Wind Serpent dataset entry (`Venomflayer Serpent`) so it no longer appears in Beast Lore filters.
- Fixed zhunter child-button visibility restore so collapsed bars no longer hard-hide children (`ztrack` / `ztrap` expansion regression).
- Fixed pet tame metadata fallback so existing pets are not assigned current zone/time unless a real tame attempt is pending.
- Fixed Smart Ammo options copy mismatch by clarifying junk-shot swap behavior and restoring ammo target as previous equipped ammo.
- Fixed Smart Ammo default state so junk-shot swaps are enabled when no saved value exists.
- Fixed Beast Training startup prompt behavior so low-level hunters no longer receive training-scan warnings before level threshold.
- Restored hidden `/mth err` slash command behavior for debug-frame access.
- Fixed `zammo` children refresh on bag/inventory updates so newly acquired ammo types appear without `/reload`.
- Preserved `zammo` out-of-stock behavior so previously shown ammo entries still remain visible with zero/out state.
- Fixed intermittent `ztrack`/`ztrap` right-click expand/collapse issue after fresh login by preventing duplicate child-frame recreation during startup.
- Fixed `zpet` spell resolution after trainer updates by using robust spellbook lookup and immediate refresh on `LEARNED_SPELL_IN_TAB`.
## [1.0.3] - 2026-02-24
### Added
- Initial published release.
+1 -1
View File
@@ -1,5 +1,5 @@
## Interface: 11200 ## Interface: 11200
## Version: 1.0.4 ## Version: 1.0.6
## Title: MetaHunt - |cff00ff00Hunter ## Title: MetaHunt - |cff00ff00Hunter
## Author: Metasploit and his Copilot ;) ## 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. ## Notes: Unified addon suite for huntards making old addons compatible with TurtleWoW, and adding an arsenal of never seen Hunter's utilities.
+1 -1
View File
@@ -1,5 +1,5 @@
MTH_CONST = MTH_CONST or {} MTH_CONST = MTH_CONST or {}
MTH_CONST.version = MTH_CONST.version or "1.0.4" MTH_CONST.version = MTH_CONST.version or "1.0.6"
MTH_CONST.WEAPON_TYPES = { MTH_CONST.WEAPON_TYPES = {
BOWS = "Bows", BOWS = "Bows",
+6 -3
View File
@@ -526,6 +526,7 @@ local function MTH_FEED_CoreDropItemOnUnitHook(unit)
end end
if target == "pet" and CursorHasItem and CursorHasItem() then if target == "pet" and CursorHasItem and CursorHasItem() then
MTH_FEED_EnsureTrackerFrame()
local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot() local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot()
local pet = MTH_FEED_GetCurrentPetContext() local pet = MTH_FEED_GetCurrentPetContext()
if not itemId then if not itemId then
@@ -608,7 +609,6 @@ function MTH_FEED_DebugStatus()
end end
function MTH_FEED_ReinstallCoreTracking() function MTH_FEED_ReinstallCoreTracking()
MTH_FEED_EnsureTrackerFrame()
local ok = MTH_FEED_InstallCoreHooks() local ok = MTH_FEED_InstallCoreHooks()
return ok and true or false return ok and true or false
end end
@@ -624,8 +624,12 @@ local function MTH_FEED_EnsureBootstrapFrame()
frame:RegisterEvent("VARIABLES_LOADED") frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("PLAYER_LOGIN") frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD") frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:SetScript("OnEvent", function() frame:SetScript("OnEvent", function(self)
MTH_FEED_ReinstallCoreTracking() MTH_FEED_ReinstallCoreTracking()
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
end
end) end)
MTH_FEED_BootstrapFrame = frame MTH_FEED_BootstrapFrame = frame
return frame return frame
@@ -1542,6 +1546,5 @@ function MTH_FEED_DebugDumpCurrentPet(limit)
return true return true
end end
MTH_FEED_EnsureTrackerFrame()
MTH_FEED_InstallCoreHooks() MTH_FEED_InstallCoreHooks()
MTH_FEED_EnsureBootstrapFrame() MTH_FEED_EnsureBootstrapFrame()
+51 -44
View File
@@ -1,5 +1,5 @@
MTH = MTH or { MTH = MTH or {
version = "1.0.4", version = "1.0.6",
name = "MetaHunt", name = "MetaHunt",
modules = {}, modules = {},
config = {}, config = {},
@@ -16,6 +16,7 @@ local MTH_MESSAGE_DEFAULTS = {
spellbookScan = false, spellbookScan = false,
petRanAway = true, petRanAway = true,
mapMarkers = true, mapMarkers = true,
stableScan = true,
} }
local function MTH_ClassGateTrace(_step, _detail) local function MTH_ClassGateTrace(_step, _detail)
@@ -424,9 +425,37 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
local resolvedName = MTH_NormalizeModuleName(name) local resolvedName = MTH_NormalizeModuleName(name)
local candidates = MTH_ModuleNameCandidates(name) local candidates = MTH_ModuleNameCandidates(name)
if type(MTH_CharSavedVariables) ~= "table" then
MTH_CharSavedVariables = {}
end
if type(MTH_CharSavedVariables.moduleStates) ~= "table" then
MTH_CharSavedVariables.moduleStates = {}
end
if type(MTH_CharSavedVariables.modules) ~= "table" then
MTH_CharSavedVariables.modules = {}
end
if type(MTH_SavedVariables) ~= "table" then if type(MTH_SavedVariables) ~= "table" then
MTH_ModuleStateDebug("read " .. tostring(name) .. " no-savedvars fallback-default=" .. tostring(defaultEnabled and true or false)) MTH_SavedVariables = {}
return defaultEnabled and true or false end
if type(MTH_SavedVariables.moduleStates) ~= "table" then
MTH_SavedVariables.moduleStates = {}
end
if type(MTH_SavedVariables.modules) ~= "table" then
MTH_SavedVariables.modules = {}
end
if type(MTH_CharSavedVariables.moduleStates) == "table"
then
for i = 1, table.getn(candidates) do
local key = candidates[i]
if MTH_CharSavedVariables.moduleStates[key] ~= nil then
local resolved = MTH_CharSavedVariables.moduleStates[key] and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from char.moduleStates." .. tostring(key) .. "=" .. tostring(resolved))
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
return resolved
end
end
end end
if type(MTH_SavedVariables.moduleStates) == "table" then if type(MTH_SavedVariables.moduleStates) == "table" then
@@ -435,25 +464,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
if MTH_SavedVariables.moduleStates[key] ~= nil then if MTH_SavedVariables.moduleStates[key] ~= nil then
local resolved = MTH_SavedVariables.moduleStates[key] and true or false local resolved = MTH_SavedVariables.moduleStates[key] and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from moduleStates." .. tostring(key) .. "=" .. tostring(resolved)) MTH_ModuleStateDebug("read " .. tostring(name) .. " from moduleStates." .. tostring(key) .. "=" .. tostring(resolved))
MTH_SavedVariables.moduleStates[resolvedName] = resolved
return resolved
end
end
end
if type(MTH_CharSavedVariables) == "table"
and type(MTH_CharSavedVariables.moduleStates) == "table"
then
for i = 1, table.getn(candidates) do
local key = candidates[i]
if MTH_CharSavedVariables.moduleStates[key] ~= nil then
local resolved = MTH_CharSavedVariables.moduleStates[key] and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from char.moduleStates." .. tostring(key) .. "=" .. tostring(resolved))
if type(MTH_SavedVariables.moduleStates) ~= "table" then
MTH_SavedVariables.moduleStates = {}
end
MTH_SavedVariables.moduleStates[resolvedName] = resolved
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
MTH_CharSavedVariables.modules[resolvedName] = {}
end
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
return resolved return resolved
end end
end end
@@ -465,9 +480,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
if type(MTH_SavedVariables.modules[key]) == "table" and MTH_SavedVariables.modules[key].enabled ~= nil then if type(MTH_SavedVariables.modules[key]) == "table" and MTH_SavedVariables.modules[key].enabled ~= nil then
local resolved = MTH_SavedVariables.modules[key].enabled and true or false local resolved = MTH_SavedVariables.modules[key].enabled and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from modules." .. tostring(key) .. ".enabled=" .. tostring(resolved)) MTH_ModuleStateDebug("read " .. tostring(name) .. " from modules." .. tostring(key) .. ".enabled=" .. tostring(resolved))
if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables.modules[key] if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
MTH_CharSavedVariables.modules[resolvedName] = {}
end end
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
return resolved return resolved
end end
end end
@@ -478,16 +495,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
if type(MTH_SavedVariables[key]) == "table" and MTH_SavedVariables[key].enabled ~= nil then if type(MTH_SavedVariables[key]) == "table" and MTH_SavedVariables[key].enabled ~= nil then
local resolved = MTH_SavedVariables[key].enabled and true or false local resolved = MTH_SavedVariables[key].enabled and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from legacy-top-level." .. tostring(key) .. ".enabled=" .. tostring(resolved)) MTH_ModuleStateDebug("read " .. tostring(name) .. " from legacy-top-level." .. tostring(key) .. ".enabled=" .. tostring(resolved))
if type(MTH_SavedVariables.modules) ~= "table" then MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
MTH_SavedVariables.modules = {} if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
MTH_CharSavedVariables.modules[resolvedName] = {}
end end
if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables[key]
end
if type(MTH_SavedVariables.moduleStates) ~= "table" then
MTH_SavedVariables.moduleStates = {}
end
MTH_SavedVariables.moduleStates[resolvedName] = resolved
return resolved return resolved
end end
end end
@@ -506,27 +518,22 @@ local function MTH_SetPersistedModuleEnabled(name, enabled)
if type(MTH_SavedVariables) ~= "table" then if type(MTH_SavedVariables) ~= "table" then
MTH_SavedVariables = {} MTH_SavedVariables = {}
end end
if type(MTH_SavedVariables.moduleStates) ~= "table" then
MTH_SavedVariables.moduleStates = {}
end
if type(MTH_SavedVariables.modules) ~= "table" then
MTH_SavedVariables.modules = {}
end
if type(MTH_SavedVariables.modules[name]) ~= "table" then
MTH_SavedVariables.modules[name] = {}
end
if type(MTH_CharSavedVariables) ~= "table" then if type(MTH_CharSavedVariables) ~= "table" then
MTH_CharSavedVariables = {} MTH_CharSavedVariables = {}
end end
if type(MTH_CharSavedVariables.moduleStates) ~= "table" then if type(MTH_CharSavedVariables.moduleStates) ~= "table" then
MTH_CharSavedVariables.moduleStates = {} MTH_CharSavedVariables.moduleStates = {}
end end
if type(MTH_CharSavedVariables.modules) ~= "table" then
MTH_CharSavedVariables.modules = {}
end
if type(MTH_CharSavedVariables.modules[name]) ~= "table" then
MTH_CharSavedVariables.modules[name] = {}
end
enabled = enabled and true or false enabled = enabled and true or false
MTH_SavedVariables.moduleStates[name] = enabled
MTH_SavedVariables.modules[name].enabled = enabled
MTH_SavedVariables[name] = MTH_SavedVariables.modules[name]
MTH_CharSavedVariables.moduleStates[name] = enabled MTH_CharSavedVariables.moduleStates[name] = enabled
MTH_CharSavedVariables.modules[name].enabled = enabled
MTH_ModuleStateDebug("write " .. tostring(name) .. " enabled=" .. tostring(enabled)) MTH_ModuleStateDebug("write " .. tostring(name) .. " enabled=" .. tostring(enabled))
end end
+34
View File
@@ -20,6 +20,7 @@ end
local MTH_PT_ApplyScan local MTH_PT_ApplyScan
local MTH_PT_AbilityHasPositiveRanks local MTH_PT_AbilityHasPositiveRanks
local MTH_PT_PromoteUnrankedKnownAliases local MTH_PT_PromoteUnrankedKnownAliases
local MTH_PT_BootstrapFrame = nil
local MTH_PT_VERBOSE_SCAN_LOGS = false local MTH_PT_VERBOSE_SCAN_LOGS = false
@@ -846,6 +847,38 @@ function MTH_PT_InitService()
end) end)
end end
function MTH_PT_InitBootstrap()
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("trainscan-bootstrap") then
return
end
if MTH_PetTrainingFrame or MTH_PT_BootstrapFrame then
return
end
local frame = CreateFrame("Frame")
if not frame then
return
end
frame:RegisterEvent("PET_TRAINING_SHOW")
frame:RegisterEvent("CRAFT_SHOW")
frame:SetScript("OnEvent", function(self, evt)
evt = evt or event
if evt ~= "PET_TRAINING_SHOW" and evt ~= "CRAFT_SHOW" then
return
end
MTH_PT_InitService()
if type(MTH_PT_OnEvent) == "function" then
MTH_PT_OnEvent(MTH_PetTrainingFrame, evt)
end
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
end
MTH_PT_BootstrapFrame = nil
end)
MTH_PT_BootstrapFrame = frame
end
function MTH_PT_ShutdownService(_reason) function MTH_PT_ShutdownService(_reason)
if not MTH_PetTrainingFrame then if not MTH_PetTrainingFrame then
return return
@@ -929,3 +962,4 @@ end
MTH_TR_InitService = MTH_PT_InitService MTH_TR_InitService = MTH_PT_InitService
MTH_TR_ShutdownService = MTH_PT_ShutdownService MTH_TR_ShutdownService = MTH_PT_ShutdownService
MTH_TR_InitBootstrap = MTH_PT_InitBootstrap
+30 -1
View File
@@ -7,6 +7,7 @@ local MTH_PS_LastScanAt = 0
local MTH_PS_VERBOSE_LOGS = false local MTH_PS_VERBOSE_LOGS = false
local MTH_PS_ForceScanUntil = 0 local MTH_PS_ForceScanUntil = 0
local MTH_PS_ForceScanBudget = 0 local MTH_PS_ForceScanBudget = 0
local MTH_PS_EventThrottle = {}
local function MTH_PS_ClearForceWindow() local function MTH_PS_ClearForceWindow()
MTH_PS_ForceScanUntil = 0 MTH_PS_ForceScanUntil = 0
@@ -26,9 +27,25 @@ end
local function MTH_PS_IsFollowupEventTrigger(trigger) local function MTH_PS_IsFollowupEventTrigger(trigger)
local t = tostring(trigger or "") local t = tostring(trigger or "")
return t == "event:UNIT_PET" return t == "event:UNIT_PET"
or t == "UNIT_PET"
or t == "event:PET_BAR_UPDATE" or t == "event:PET_BAR_UPDATE"
or t == "PET_BAR_UPDATE"
or t == "event:SPELLS_CHANGED" or t == "event:SPELLS_CHANGED"
or t == "SPELLS_CHANGED"
or t == "event:LEARNED_SPELL_IN_TAB" or t == "event:LEARNED_SPELL_IN_TAB"
or t == "LEARNED_SPELL_IN_TAB"
end
local function MTH_PS_ShouldHandleEvent(evt, minIntervalSeconds)
local now = tonumber(GetTime and GetTime() or time()) or 0
local key = tostring(evt or "")
local minInterval = tonumber(minIntervalSeconds) or 0
local last = tonumber(MTH_PS_EventThrottle[key] or 0) or 0
if minInterval > 0 and (now - last) < minInterval then
return false
end
MTH_PS_EventThrottle[key] = now
return true
end end
local function MTH_PS_IsPriorityTrigger(trigger) local function MTH_PS_IsPriorityTrigger(trigger)
@@ -237,7 +254,19 @@ local function MTH_PS_OnEvent(_, evt, eventArg1)
end end
if evt == "PLAYER_ENTERING_WORLD" or evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" or evt == "SPELLS_CHANGED" or evt == "LEARNED_SPELL_IN_TAB" then if evt == "PLAYER_ENTERING_WORLD" or evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" or evt == "SPELLS_CHANGED" or evt == "LEARNED_SPELL_IN_TAB" then
MTH_PSP_RequestScan("event:" .. tostring(evt), 1) local minInterval = 1
if evt == "PET_BAR_UPDATE" then
minInterval = 2
elseif evt == "SPELLS_CHANGED" then
minInterval = 2.5
elseif evt == "LEARNED_SPELL_IN_TAB" then
minInterval = 0.75
elseif evt == "UNIT_PET" then
minInterval = 0.5
end
if MTH_PS_ShouldHandleEvent(evt, minInterval) then
MTH_PSP_RequestScan(evt, 1)
end
end end
end end
+472 -37
View File
@@ -4,10 +4,10 @@ end
local MTH_PETS_SCHEMA_VERSION = 2 local MTH_PETS_SCHEMA_VERSION = 2
local MTH_PETS_CORE_HOOK_BOUNDARY_KEY = "core-pet-rename-hook" local MTH_PETS_CORE_HOOK_BOUNDARY_KEY = "core-pet-rename-hook"
local MTH_PETS_TRACE_RUNAWAY = false
MTH_PETS_TRACE_CONSISTENCY = false MTH_PETS_TRACE_CONSISTENCY = false
local MTH_StableFrame = nil local MTH_StableFrame = nil
local MTH_ST_BootstrapFrame = nil
local MTH_ST_LastAutoScanAt = 0 local MTH_ST_LastAutoScanAt = 0
local MTH_ST_LastUnitPetHadPet = false local MTH_ST_LastUnitPetHadPet = false
local MTH_PETS_LastTameAttempt = nil local MTH_PETS_LastTameAttempt = nil
@@ -15,6 +15,7 @@ local MTH_PETS_LiveState = nil
local MTH_PETS_LiveStateSeq = 0 local MTH_PETS_LiveStateSeq = 0
local MTH_PETS_LiveStateSubscribers = {} local MTH_PETS_LiveStateSubscribers = {}
local MTH_PETS_LiveStateEventName = "MTH_PET_LIVE_STATE_CHANGED" local MTH_PETS_LiveStateEventName = "MTH_PET_LIVE_STATE_CHANGED"
local MTH_PETS_EventThrottle = {}
MTH_PETS_CoreOriginal_PetRename = MTH_PETS_CoreOriginal_PetRename or nil MTH_PETS_CoreOriginal_PetRename = MTH_PETS_CoreOriginal_PetRename or nil
@@ -61,6 +62,17 @@ local function MTH_PETS_RequestPetSpellScan(reason)
end end
end end
local function MTH_PETS_ShouldHandleThrottledEvent(key, minIntervalSeconds)
local now = (type(GetTime) == "function" and GetTime()) or (type(time) == "function" and time()) or 0
local threshold = tonumber(minIntervalSeconds) or 0
local last = tonumber(MTH_PETS_EventThrottle[key] or 0) or 0
if threshold > 0 and (now - last) < threshold then
return false
end
MTH_PETS_EventThrottle[key] = now
return true
end
local function MTH_PETS_CopyLiveState(state) local function MTH_PETS_CopyLiveState(state)
if type(state) ~= "table" then if type(state) ~= "table" then
return nil return nil
@@ -146,7 +158,10 @@ local function MTH_PETS_NotifyLiveSubscribers(state, source)
end end
function MTH_PETS_EmitLiveState(source, force) function MTH_PETS_EmitLiveState(source, force)
local emittedSource = tostring(source or "stablemaster") local emittedSource = source or "stablemaster"
if type(emittedSource) ~= "string" then
emittedSource = tostring(emittedSource)
end
local nextState = MTH_PETS_BuildLiveState(emittedSource) local nextState = MTH_PETS_BuildLiveState(emittedSource)
local changed = force and true or (not MTH_PETS_AreLiveStatesEqual(MTH_PETS_LiveState, nextState)) local changed = force and true or (not MTH_PETS_AreLiveStatesEqual(MTH_PETS_LiveState, nextState))
if not changed then if not changed then
@@ -373,6 +388,17 @@ local function MTH_PETS_NormalizeSystemMessage(rawMessage)
return message return message
end end
local function MTH_PETS_NormalizeSystemMessageKey(rawMessage)
local message = MTH_PETS_NormalizeSystemMessage(rawMessage)
message = string.lower(tostring(message or ""))
message = string.gsub(message, "%%s", " ")
message = string.gsub(message, "[^%w%s]", " ")
message = string.gsub(message, "%s+", " ")
message = string.gsub(message, "^%s+", "")
message = string.gsub(message, "%s+$", "")
return message
end
local function MTH_PETS_IsRunawaySystemMessage(rawMessage) local function MTH_PETS_IsRunawaySystemMessage(rawMessage)
local message = MTH_PETS_NormalizeSystemMessage(rawMessage) local message = MTH_PETS_NormalizeSystemMessage(rawMessage)
if message == "" then if message == "" then
@@ -380,8 +406,20 @@ local function MTH_PETS_IsRunawaySystemMessage(rawMessage)
end end
local lostPetMessage = _G and _G["PETTAME_LOSTPET"] local lostPetMessage = _G and _G["PETTAME_LOSTPET"]
if type(lostPetMessage) == "string" and lostPetMessage ~= "" and message == lostPetMessage then if type(lostPetMessage) == "string" and lostPetMessage ~= "" then
return true if message == lostPetMessage then
return true
end
local messageKey = MTH_PETS_NormalizeSystemMessageKey(message)
local lostKey = MTH_PETS_NormalizeSystemMessageKey(lostPetMessage)
if messageKey ~= "" and lostKey ~= "" then
if messageKey == lostKey then
return true
end
if string.find(messageKey, lostKey, 1, true) then
return true
end
end
end end
local lower = string.lower(message) local lower = string.lower(message)
@@ -389,14 +427,12 @@ local function MTH_PETS_IsRunawaySystemMessage(rawMessage)
if string.find(lower, "pet ran away", 1, true) then return true end if string.find(lower, "pet ran away", 1, true) then return true end
if string.find(lower, "pet has fled", 1, true) then return true end if string.find(lower, "pet has fled", 1, true) then return true end
if string.find(lower, "pet fled", 1, true) then return true end if string.find(lower, "pet fled", 1, true) then return true end
if string.find(lower, "pet has left", 1, true) then return true end
if string.find(lower, "pet left", 1, true) then return true end
return false return false
end end
local function MTH_PETS_TraceRunawayEvent(evt, rawMessage, matched)
return
end
local function MTH_PETS_ParseCreatureIdFromGuid(guid) local function MTH_PETS_ParseCreatureIdFromGuid(guid)
local guidText = tostring(guid or "") local guidText = tostring(guid or "")
if guidText == "" then if guidText == "" then
@@ -700,6 +736,38 @@ local function MTH_PETS_GetPendingTameBeastId(snapshot)
return nil return nil
end end
local function MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
if type(row) ~= "table" then
return nil
end
local pendingResolved = MTH_PETS_GetPendingTameBeastId(snapshot)
if pendingResolved and pendingResolved > 0 then
return pendingResolved
end
local fromSnapshot = snapshot and tonumber(snapshot.beastId) or nil
if fromSnapshot and fromSnapshot > 0 then
return fromSnapshot
end
local fromRow = tonumber(row.beastId)
if fromRow and fromRow > 0 then
return fromRow
end
local lookup = MTH_PETS_FindBeastIdByDataset(
tostring((snapshot and snapshot.name) or row.name or ""),
tostring((snapshot and snapshot.family) or row.family or ""),
tonumber((snapshot and snapshot.level) or row.level)
)
if lookup and lookup > 0 then
return lookup
end
return nil
end
local function MTH_PETS_MakeSignature(name, family, level) local function MTH_PETS_MakeSignature(name, family, level)
local cleanName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name)) local cleanName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name))
local cleanFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family)) local cleanFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family))
@@ -707,6 +775,114 @@ local function MTH_PETS_MakeSignature(name, family, level)
return cleanName .. "|" .. cleanFamily .. "|" .. tostring(numericLevel) return cleanName .. "|" .. cleanFamily .. "|" .. tostring(numericLevel)
end end
local function MTH_PETS_RowHasTameRecord(row)
if type(row) ~= "table" then
return false
end
if row.tameRecorded == true then
return true
end
if tonumber(row.tamedAt) and tonumber(row.tamedAt) > 0 then
return true
end
if tonumber(row.tameBeastId) and tonumber(row.tameBeastId) > 0 then
return true
end
if type(row.tameZone) == "string" and row.tameZone ~= "" then
return true
end
return false
end
local function MTH_PETS_BackfillTameMetadataFromEvents(row)
if type(row) ~= "table" then
return false
end
if MTH_PETS_RowHasTameRecord(row) then
return false
end
if type(row.events) ~= "table" then
return false
end
local bestEvent = nil
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
local evAt = tonumber(ev.at) or 0
local timelineOk = true
if stableFirstSeenAt > 0 and evAt > 0 and evAt > stableFirstSeenAt then
-- If we only saw this pet as stabled before the acquire event, it was likely a call-out, not a tame.
timelineOk = false
end
if timelineOk then
if not bestEvent then
bestEvent = ev
else
local bestAt = tonumber(bestEvent.at) or 0
if bestAt <= 0 or (evAt > 0 and evAt < bestAt) then
bestEvent = ev
end
end
end
end
end
if type(bestEvent) ~= "table" then
return false
end
local context = type(bestEvent.context) == "table" and bestEvent.context or nil
local changed = false
if row.tamedAt == nil or tonumber(row.tamedAt) == nil or tonumber(row.tamedAt) <= 0 then
local candidateAt = tonumber(bestEvent.at) or (context and tonumber(context.timestamp) or nil) or tonumber(row.createdAt)
if candidateAt and candidateAt > 0 then
row.tamedAt = candidateAt
changed = true
end
end
if context then
if row.tameHunterLevel == nil and context.hunterLevel ~= nil then
row.tameHunterLevel = context.hunterLevel
changed = true
end
if (row.tameZone == nil or row.tameZone == "") and context.zone ~= nil and tostring(context.zone) ~= "" then
row.tameZone = context.zone
changed = true
end
if (row.tameSubZone == nil or row.tameSubZone == "") and context.subZone ~= nil and tostring(context.subZone) ~= "" then
row.tameSubZone = context.subZone
changed = true
end
if row.tameX == nil and context.x ~= nil then
row.tameX = context.x
changed = true
end
if row.tameY == nil and context.y ~= nil then
row.tameY = context.y
changed = true
end
end
if (row.tameBeastId == nil or tonumber(row.tameBeastId) == nil) and row.beastId ~= nil then
local beastId = tonumber(row.beastId)
if beastId and beastId > 0 then
row.tameBeastId = beastId
changed = true
end
end
if changed then
row.tameRecorded = true
row.lastUpdated = time()
end
return changed
end
local function MTH_PETS_EnsurePetStoreSchema(pets) local function MTH_PETS_EnsurePetStoreSchema(pets)
if type(pets.petStore) ~= "table" then if type(pets.petStore) ~= "table" then
pets.petStore = {} pets.petStore = {}
@@ -750,6 +926,7 @@ local function MTH_PETS_EnsurePetStoreSchema(pets)
end end
row.stableInfo.loyalty = nil row.stableInfo.loyalty = nil
end end
MTH_PETS_BackfillTameMetadataFromEvents(row)
end end
end end
end end
@@ -902,6 +1079,9 @@ local function MTH_PETS_EnsureSchema(pets)
if pets.updatedAt == nil then if pets.updatedAt == nil then
pets.updatedAt = 0 pets.updatedAt = 0
end end
if pets.createdAt == nil or tonumber(pets.createdAt) == nil or tonumber(pets.createdAt) <= 0 then
pets.createdAt = now
end
if pets.currentPetId == nil then if pets.currentPetId == nil then
local cpId = (type(pets.currentPet) == "table") and pets.currentPet.id or nil local cpId = (type(pets.currentPet) == "table") and pets.currentPet.id or nil
pets.currentPetId = MTH_PETS_NormalizePetId(cpId) pets.currentPetId = MTH_PETS_NormalizePetId(cpId)
@@ -1227,8 +1407,11 @@ local function MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context)
row.guid = snapshot.guid or row.guid row.guid = snapshot.guid or row.guid
row.beastId = snapshot.beastId or row.beastId row.beastId = snapshot.beastId or row.beastId
local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table" local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table"
if hasPendingTame and snapshot.beastId and (row.tameBeastId == nil or tonumber(row.tameBeastId) == nil) then if hasPendingTame and (row.tameBeastId == nil or tonumber(row.tameBeastId) == nil) then
row.tameBeastId = snapshot.beastId local resolvedTameBeastId = MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
if resolvedTameBeastId and resolvedTameBeastId > 0 then
row.tameBeastId = resolvedTameBeastId
end
end end
if hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet") and type(context) == "table" then if hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet") and type(context) == "table" then
row.tameRecorded = true row.tameRecorded = true
@@ -1277,8 +1460,11 @@ local function MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context)
row.tameSubZone = row.tameSubZone or context.subZone row.tameSubZone = row.tameSubZone or context.subZone
row.tameX = row.tameX or context.x row.tameX = row.tameX or context.x
row.tameY = row.tameY or context.y row.tameY = row.tameY or context.y
if snapshot.beastId and tonumber(row.tameBeastId) == nil then if tonumber(row.tameBeastId) == nil then
row.tameBeastId = snapshot.beastId local resolvedTameBeastId = MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
if resolvedTameBeastId and resolvedTameBeastId > 0 then
row.tameBeastId = resolvedTameBeastId
end
end end
end end
end end
@@ -1933,6 +2119,7 @@ function MTH_PETS_RefreshCurrentPet()
local cp = pets.currentPet local cp = pets.currentPet
local previousCurrentId = cp and cp.id or nil local previousCurrentId = cp and cp.id or nil
local hadPetBeforeRefresh = MTH_ST_LastUnitPetHadPet and true or false
local now = time() local now = time()
local snapshot = MTH_PETS_MakeSnapshotFromLivePet() local snapshot = MTH_PETS_MakeSnapshotFromLivePet()
if pets.currentPetSuppressed == true and pets.currentPetSuppressedAwaitNoLive == true then if pets.currentPetSuppressed == true and pets.currentPetSuppressedAwaitNoLive == true then
@@ -1976,6 +2163,7 @@ function MTH_PETS_RefreshCurrentPet()
MTH_PETS_SetCurrentPetId(pets, keepCurrentId) MTH_PETS_SetCurrentPetId(pets, keepCurrentId)
MTH_PETS_LogConsistency("RefreshCurrentPet no-live result current=" .. MTH_PETS_FormatCurrentConsistency(cp) MTH_PETS_LogConsistency("RefreshCurrentPet no-live result current=" .. MTH_PETS_FormatCurrentConsistency(cp)
.. " activeCurrentId=" .. tostring(petStore and petStore.activeCurrentId or nil)) .. " activeCurrentId=" .. tostring(petStore and petStore.activeCurrentId or nil))
MTH_ST_LastUnitPetHadPet = false
pets.updatedAt = now pets.updatedAt = now
return return
end end
@@ -2012,11 +2200,54 @@ function MTH_PETS_RefreshCurrentPet()
if cp.id and cp.id ~= previousCurrentId then if cp.id and cp.id ~= previousCurrentId then
MTH_PETS_RequestPetSpellScan("refresh-current-change") MTH_PETS_RequestPetSpellScan("refresh-current-change")
end end
local row = MTH_PETS_GetPetById(pets, petId)
if type(row) == "table" then
if type(row.events) ~= "table" then
row.events = {}
end
local eventType = "pet-updated"
if created and (not hadPetBeforeRefresh) then
eventType = "pet-acquired"
end
local eventContext = MTH_PETS_CaptureContext()
table.insert(row.events, {
type = eventType,
source = "refresh-current-pet",
at = now,
context = eventContext,
})
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 if hadPendingTame and cp.id and cp.id ~= previousCurrentId then
MTH_PETS_LogTame("RefreshCurrentPet acquire/change completed; clearing pending tame attempt") MTH_PETS_LogTame("RefreshCurrentPet acquire/change completed; clearing pending tame attempt")
MTH_PETS_LastTameAttempt = nil MTH_PETS_LastTameAttempt = nil
end end
MTH_PETS_LogConsistency("RefreshCurrentPet live result current=" .. MTH_PETS_FormatCurrentConsistency(cp)) MTH_PETS_LogConsistency("RefreshCurrentPet live result current=" .. MTH_PETS_FormatCurrentConsistency(cp))
MTH_ST_LastUnitPetHadPet = true
pets.updatedAt = now pets.updatedAt = now
end end
@@ -2283,6 +2514,7 @@ function MTH_ST_Scan(reason)
local slotCount = tonumber(GetNumStableSlots()) or 0 local slotCount = tonumber(GetNumStableSlots()) or 0
local store = MTH_ST_GetStore() local store = MTH_ST_GetStore()
local petsRoot = MTH_PETS_GetRootStore() local petsRoot = MTH_PETS_GetRootStore()
MTH_PETS_RefreshCurrentPet()
local scanContext = MTH_PETS_CaptureContext() local scanContext = MTH_PETS_CaptureContext()
local scanStableMasterName = MTH_PETS_GetStableMasterName() local scanStableMasterName = MTH_PETS_GetStableMasterName()
store.lastScan = time() store.lastScan = time()
@@ -2314,6 +2546,28 @@ function MTH_ST_Scan(reason)
} }
end end
local c1, c2, c3, c4, c5, c6, c7, c8 = GetStablePetInfo(0)
local currentPetId = (type(petsRoot) == "table" and type(petsRoot.currentPet) == "table") and petsRoot.currentPet.id or nil
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
local currentIcon = tostring(c1 or "")
if currentIcon ~= "" then
currentRow.icon = currentIcon
currentRow.lastUpdated = time()
end
end
end
store.current = {
petId = currentPetId,
icon = c1,
name = c2,
level = c3,
family = c4,
loyalty = c5,
raw = { c1, c2, c3, c4, c5, c6, c7, c8 },
}
MTH_PETS_MarkStableVisited(petsRoot, tostring(reason or "stable-scan")) MTH_PETS_MarkStableVisited(petsRoot, tostring(reason or "stable-scan"))
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
@@ -2524,6 +2778,54 @@ local function MTH_ST_DebugDumpLine(line)
end end
end end
local MTH_PETS_DebugBaselineSnapshot = nil
local function MTH_ST_DebugCloneValue(value, seen)
if type(value) ~= "table" then
return value
end
seen = seen or {}
if seen[value] then
return seen[value]
end
local clone = {}
seen[value] = clone
for key, child in pairs(value) do
clone[MTH_ST_DebugCloneValue(key, seen)] = MTH_ST_DebugCloneValue(child, seen)
end
return clone
end
local function MTH_PETS_CaptureDebugSnapshot(targetPetId)
local pets = MTH_PETS_GetRootStore()
if type(pets) ~= "table" then
return nil, "pets datastore unavailable"
end
MTH_PETS_RefreshCurrentPet()
local cp = (type(pets.currentPet) == "table") and pets.currentPet or nil
local petId = targetPetId or (cp and cp.id) or pets.currentPetId
if petId == nil or tostring(petId) == "" then
return nil, "no current pet id"
end
local row = MTH_PETS_GetPetById(pets, petId)
if type(row) ~= "table" then
return nil, "active row missing for pet id=" .. tostring(petId)
end
local snapshot = {
capturedAt = time(),
petId = tostring(petId),
currentPetId = pets.currentPetId,
currentPet = MTH_ST_DebugCloneValue(cp or {}),
row = MTH_ST_DebugCloneValue(row),
stableScan = MTH_ST_DebugCloneValue(pets.stableScan or {}),
}
return snapshot, nil
end
local function MTH_ST_DebugSortKeys(tbl) local function MTH_ST_DebugSortKeys(tbl)
local keys = {} local keys = {}
for k in pairs(tbl or {}) do for k in pairs(tbl or {}) do
@@ -2753,6 +3055,64 @@ function MTH_CommandPetsDump()
end end
end end
function MTH_CommandPetsSnap()
local snapshot, err = MTH_PETS_CaptureDebugSnapshot(nil)
if not snapshot then
MTH:Print("Pet snapshot failed: " .. tostring(err or "unknown"))
return false
end
MTH_PETS_DebugBaselineSnapshot = snapshot
MTH:Print("Pet snapshot saved for id=" .. tostring(snapshot.petId) .. ". Run /mth petsdiff after reproducing the issue.")
MTH_ST_DebugDumpLine("[PETSNAP] BASELINE BEGIN")
MTH_ST_DebugDumpValue("[PETSNAP].baseline", snapshot, 0, 5, {})
MTH_ST_DebugDumpLine("[PETSNAP] BASELINE END")
return true
end
function MTH_CommandPetsDiff()
if type(MTH_PETS_DebugBaselineSnapshot) ~= "table" then
MTH:Print("No pet snapshot baseline found. Run /mth petssnap first.")
return false
end
local baseline = MTH_PETS_DebugBaselineSnapshot
local current, err = MTH_PETS_CaptureDebugSnapshot(baseline.petId)
if not current then
MTH:Print("Pet snapshot compare failed: " .. tostring(err or "unknown"))
return false
end
MTH:Print("Pet snapshot compare for id=" .. tostring(current.petId) .. ".")
MTH_ST_DebugDumpLine("[PETSNAP] BEFORE BEGIN")
MTH_ST_DebugDumpValue("[PETSNAP].before", baseline, 0, 5, {})
MTH_ST_DebugDumpLine("[PETSNAP] BEFORE END")
MTH_ST_DebugDumpLine("[PETSNAP] AFTER BEGIN")
MTH_ST_DebugDumpValue("[PETSNAP].after", current, 0, 5, {})
MTH_ST_DebugDumpLine("[PETSNAP] AFTER END")
local diffKeys = {
"name", "family", "level", "beastId", "icon",
"tameRecorded", "tamedAt", "tameZone", "tameSubZone", "tameX", "tameY", "tameBeastId",
"stableSlot", "stabledAt", "stableFirstSeenAt", "lastUnstabledAt", "lastSource", "lastUpdated",
}
local diffCount = 0
for i = 1, table.getn(diffKeys) do
local key = diffKeys[i]
local beforeValue = baseline.row and baseline.row[key] or nil
local afterValue = current.row and current.row[key] or nil
if tostring(beforeValue) ~= tostring(afterValue) then
diffCount = diffCount + 1
MTH_ST_DebugDumpLine("[PETSNAP] DIFF row." .. tostring(key) .. ": " .. tostring(beforeValue) .. " -> " .. tostring(afterValue))
end
end
if diffCount <= 0 then
MTH_ST_DebugDumpLine("[PETSNAP] DIFF none")
end
MTH_PETS_DebugBaselineSnapshot = current
return true
end
function MTH_CommandStableScan() function MTH_CommandStableScan()
if UnitClass then if UnitClass then
local _, classToken = UnitClass("player") local _, classToken = UnitClass("player")
@@ -2773,9 +3133,10 @@ function MTH_CommandStableScan()
MTH:Print("Stable scan captured " .. tostring(slotCount) .. " slot(s).") MTH:Print("Stable scan captured " .. tostring(slotCount) .. " slot(s).")
end end
local function MTH_ST_OnEvent(_, evt, eventArg1) local function MTH_ST_OnEvent(_, evt, eventArg1, eventArg2)
evt = evt or event evt = evt or event
eventArg1 = eventArg1 or arg1 eventArg1 = eventArg1 or arg1
eventArg2 = eventArg2 or arg2
if not evt then if not evt then
return return
end end
@@ -2784,35 +3145,43 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
local pets = MTH_PETS_GetRootStore() local pets = MTH_PETS_GetRootStore()
MTH_PETS_MaybePromptStableVisit(pets) MTH_PETS_MaybePromptStableVisit(pets)
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PLAYER_ENTERING_WORLD", true) MTH_PETS_EmitLiveState("PLAYER_ENTERING_WORLD", true)
return return
end end
if evt == "SPELLCAST_CHANNEL_START" then local isUnitSpellcast = (evt == "UNIT_SPELLCAST_START" or evt == "UNIT_SPELLCAST_STOP"
MTH_PETS_RecordTameAttempt("event:" .. tostring(evt), eventArg1) or evt == "UNIT_SPELLCAST_FAILED" or evt == "UNIT_SPELLCAST_INTERRUPTED"
or evt == "UNIT_SPELLCAST_CHANNEL_START" or evt == "UNIT_SPELLCAST_CHANNEL_STOP")
local castSpellName = eventArg1
if isUnitSpellcast then
if eventArg1 ~= "player" then
return
end
castSpellName = eventArg2
end
if evt == "SPELLCAST_START" or evt == "SPELLCAST_CHANNEL_START"
or evt == "UNIT_SPELLCAST_START" or evt == "UNIT_SPELLCAST_CHANNEL_START" then
MTH_PETS_RecordTameAttempt(evt, castSpellName)
return return
end end
if evt == "SPELLCAST_STOP" or evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then if evt == "SPELLCAST_STOP" or evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED"
MTH_PETS_LogTame("Spell terminal evt=" .. tostring(evt) .. " spell='" .. tostring(eventArg1 or "") .. "' pending=" .. tostring(type(MTH_PETS_LastTameAttempt) == "table")) or evt == "UNIT_SPELLCAST_STOP" or evt == "UNIT_SPELLCAST_CHANNEL_STOP"
local terminalIsTame = MTH_PETS_IsTameBeastSpellName(eventArg1) or evt == "UNIT_SPELLCAST_FAILED" or evt == "UNIT_SPELLCAST_INTERRUPTED" then
local terminalIsTame = MTH_PETS_IsTameBeastSpellName(castSpellName)
if not terminalIsTame and type(MTH_PETS_LastTameAttempt) == "table" then if not terminalIsTame and type(MTH_PETS_LastTameAttempt) == "table" then
terminalIsTame = true terminalIsTame = true
MTH_PETS_LogTame("Treating terminal event as tame because pending attempt exists")
end end
if not terminalIsTame then if not terminalIsTame then
local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName() local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName()
if fallbackName and MTH_PETS_IsTameBeastSpellName(fallbackName) then if fallbackName and MTH_PETS_IsTameBeastSpellName(fallbackName) then
terminalIsTame = true terminalIsTame = true
MTH_PETS_LogTame("Resolved terminal spell via " .. tostring(fallbackSource) .. " => '" .. tostring(fallbackName) .. "'")
end end
end end
if terminalIsTame then if terminalIsTame then
if evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then if evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then
MTH_PETS_LogTame("Tame ended with failure/interruption; clearing pending attempt")
MTH_PETS_LastTameAttempt = nil MTH_PETS_LastTameAttempt = nil
elseif evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_STOP" then
MTH_PETS_LogTame("Tame channel ended; awaiting UNIT_PET to confirm acquire")
end end
end end
return return
@@ -2822,14 +3191,17 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
if eventArg1 ~= "player" then if eventArg1 ~= "player" then
return return
end end
MTH_PETS_HandleUnitPetTransition("event:UNIT_PET") MTH_PETS_HandleUnitPetTransition("UNIT_PET")
MTH_PETS_EmitLiveState("event:UNIT_PET") MTH_PETS_EmitLiveState("UNIT_PET")
return return
end end
if evt == "PET_BAR_UPDATE" then if evt == "PET_BAR_UPDATE" then
if not MTH_PETS_ShouldHandleThrottledEvent("PET_BAR_UPDATE", 0.5) then
return
end
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_BAR_UPDATE") MTH_PETS_EmitLiveState("PET_BAR_UPDATE")
return return
end end
@@ -2837,43 +3209,61 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
if eventArg1 and eventArg1 ~= "pet" then if eventArg1 and eventArg1 ~= "pet" then
return return
end end
if not MTH_PETS_ShouldHandleThrottledEvent("UNIT_HAPPINESS", 0.75) then
return
end
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:UNIT_HAPPINESS") MTH_PETS_EmitLiveState("UNIT_HAPPINESS")
return return
end end
if evt == "CHAT_MSG_COMBAT_XP_GAIN" or evt == "PLAYER_XP_UPDATE" then if evt == "CHAT_MSG_COMBAT_XP_GAIN" or evt == "PLAYER_XP_UPDATE" then
if not MTH_PETS_ShouldHandleThrottledEvent("XP_UPDATE", 1.5) then
return
end
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:" .. tostring(evt)) MTH_PETS_EmitLiveState(evt)
return return
end end
if evt == "CHAT_MSG_SYSTEM" or evt == "UI_ERROR_MESSAGE" then if evt == "CHAT_MSG_SYSTEM" or evt == "UI_ERROR_MESSAGE"
or evt == "CHAT_MSG_SPELL_PET_INFO" or evt == "CHAT_MSG_SPELL_PET_DAMAGE"
or evt == "CHAT_MSG_COMBAT_PET_HITS" or evt == "CHAT_MSG_COMBAT_PET_MISSES" then
local matchedRunaway = MTH_PETS_IsRunawaySystemMessage(eventArg1) local matchedRunaway = MTH_PETS_IsRunawaySystemMessage(eventArg1)
MTH_PETS_TraceRunawayEvent(evt, eventArg1, matchedRunaway)
if matchedRunaway then if matchedRunaway then
MTH_PETS_RecordPetRunaway("event:" .. tostring(evt), eventArg1) MTH_PETS_RecordPetRunaway(evt, eventArg1)
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:" .. tostring(evt)) MTH_PETS_EmitLiveState(evt)
end end
return return
end end
if evt == "PET_STABLE_SHOW" then if evt == "PET_STABLE_SHOW" then
MTH_ST_RunScanThrottled("event:" .. tostring(evt), 1) local scanned = MTH_ST_RunScanThrottled(evt, 1)
if scanned and MTH and MTH.Print then
local shouldPrint = true
if MTH.IsMessageEnabled then
shouldPrint = MTH:IsMessageEnabled("stableScan", true)
end
if shouldPrint then
local store = MTH_ST_GetStore()
local slotCount = tonumber(store and store.slotCount) or 0
MTH:Print("Stable scan complete: " .. tostring(slotCount) .. " slot(s).")
end
end
return return
end end
if evt == "PET_STABLE_UPDATE" then if evt == "PET_STABLE_UPDATE" then
MTH_ST_Scan("event:PET_STABLE_UPDATE") MTH_ST_Scan(evt)
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_STABLE_UPDATE") MTH_PETS_EmitLiveState(evt)
return return
end end
if evt == "PET_STABLE_CLOSED" then if evt == "PET_STABLE_CLOSED" then
MTH_PETS_RefreshCurrentPet() MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_STABLE_CLOSED") MTH_PETS_EmitLiveState(evt)
return return
end end
end end
@@ -2889,16 +3279,28 @@ function MTH_ST_InitService()
MTH_StableFrame = CreateFrame("Frame", "MTHStableScanFrame") MTH_StableFrame = CreateFrame("Frame", "MTHStableScanFrame")
MTH_StableFrame:RegisterEvent("PLAYER_ENTERING_WORLD") MTH_StableFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
MTH_StableFrame:RegisterEvent("SPELLCAST_START")
MTH_StableFrame:RegisterEvent("SPELLCAST_STOP")
MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_START") MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_START")
MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_STOP") MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_STOP")
MTH_StableFrame:RegisterEvent("SPELLCAST_FAILED") MTH_StableFrame:RegisterEvent("SPELLCAST_FAILED")
MTH_StableFrame:RegisterEvent("SPELLCAST_INTERRUPTED") MTH_StableFrame:RegisterEvent("SPELLCAST_INTERRUPTED")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_START")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_FAILED")
MTH_StableFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
MTH_StableFrame:RegisterEvent("UNIT_PET") MTH_StableFrame:RegisterEvent("UNIT_PET")
MTH_StableFrame:RegisterEvent("PET_BAR_UPDATE") MTH_StableFrame:RegisterEvent("PET_BAR_UPDATE")
MTH_StableFrame:RegisterEvent("UNIT_HAPPINESS") MTH_StableFrame:RegisterEvent("UNIT_HAPPINESS")
MTH_StableFrame:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN") MTH_StableFrame:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN")
MTH_StableFrame:RegisterEvent("PLAYER_XP_UPDATE") MTH_StableFrame:RegisterEvent("PLAYER_XP_UPDATE")
MTH_StableFrame:RegisterEvent("CHAT_MSG_SYSTEM") MTH_StableFrame:RegisterEvent("CHAT_MSG_SYSTEM")
MTH_StableFrame:RegisterEvent("CHAT_MSG_SPELL_PET_INFO")
MTH_StableFrame:RegisterEvent("CHAT_MSG_SPELL_PET_DAMAGE")
MTH_StableFrame:RegisterEvent("CHAT_MSG_COMBAT_PET_HITS")
MTH_StableFrame:RegisterEvent("CHAT_MSG_COMBAT_PET_MISSES")
MTH_StableFrame:RegisterEvent("UI_ERROR_MESSAGE") MTH_StableFrame:RegisterEvent("UI_ERROR_MESSAGE")
MTH_StableFrame:RegisterEvent("PET_STABLE_SHOW") MTH_StableFrame:RegisterEvent("PET_STABLE_SHOW")
MTH_StableFrame:RegisterEvent("PET_STABLE_UPDATE") MTH_StableFrame:RegisterEvent("PET_STABLE_UPDATE")
@@ -2912,6 +3314,39 @@ function MTH_ST_InitService()
end end
end end
function MTH_ST_InitBootstrap()
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("stablescan-bootstrap") then
return
end
if MTH_StableFrame or MTH_ST_BootstrapFrame then
return
end
local frame = CreateFrame("Frame")
if not frame then
return
end
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PET_STABLE_SHOW")
frame:RegisterEvent("PET_STABLE_UPDATE")
frame:SetScript("OnEvent", function(self, evt)
evt = evt or event
if evt ~= "PLAYER_ENTERING_WORLD" and evt ~= "PET_STABLE_SHOW" and evt ~= "PET_STABLE_UPDATE" then
return
end
MTH_ST_InitService()
if (evt == "PET_STABLE_SHOW" or evt == "PET_STABLE_UPDATE") and type(MTH_ST_Scan) == "function" then
MTH_ST_Scan("bootstrap:" .. tostring(evt))
end
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
end
MTH_ST_BootstrapFrame = nil
end)
MTH_ST_BootstrapFrame = frame
end
function MTH_ST_ShutdownService(_reason) function MTH_ST_ShutdownService(_reason)
if not MTH_StableFrame then if not MTH_StableFrame then
return return
+77 -6
View File
@@ -129,6 +129,49 @@ local function MTH_CommandOptions()
end end
end end
local function MTH_FormatAgeSeconds(seconds)
local value = tonumber(seconds) or 0
if value < 0 then value = 0 end
if value < 60 then
return tostring(math.floor(value)) .. "s"
end
if value < 3600 then
return tostring(math.floor(value / 60)) .. "m"
end
if value < 86400 then
return tostring(math.floor(value / 3600)) .. "h"
end
return tostring(math.floor(value / 86400)) .. "d"
end
local function MTH_CommandPeers()
if not (MTH and MTH.VersionCheck and type(MTH.VersionCheck.GetTrackedPeers) == "function") then
MTH:Print("Peer tracker is not available yet")
return
end
local peers = MTH.VersionCheck:GetTrackedPeers()
local count = table.getn(peers)
if count <= 0 then
MTH:Print("No MetaHunt broadcasters seen yet on LFT")
return
end
MTH:Print("MetaHunt broadcasters seen on LFT: " .. tostring(count))
local now = (type(time) == "function" and time()) or (type(GetTime) == "function" and math.floor(GetTime())) or 0
local maxRows = math.min(count, 20)
for i = 1, maxRows do
local peer = peers[i]
if type(peer) == "table" then
local age = MTH_FormatAgeSeconds((now or 0) - (tonumber(peer.lastSeenAt) or 0))
MTH:Print("- " .. tostring(peer.name or "?") .. " v" .. tostring(peer.versionText or "?") .. " (seen " .. tostring(age) .. " ago)")
end
end
if count > maxRows then
MTH:Print("... and " .. tostring(count - maxRows) .. " more")
end
end
local function MTH_CommandPetSpellScanFallback() local function MTH_CommandPetSpellScanFallback()
local getSpellName = (type(getglobal) == "function" and getglobal("GetSpellName")) or (_G and _G["GetSpellName"]) local getSpellName = (type(getglobal) == "function" and getglobal("GetSpellName")) or (_G and _G["GetSpellName"])
local getSpellTexture = (type(getglobal) == "function" and getglobal("GetSpellTexture")) or (_G and _G["GetSpellTexture"]) local getSpellTexture = (type(getglobal) == "function" and getglobal("GetSpellTexture")) or (_G and _G["GetSpellTexture"])
@@ -241,10 +284,37 @@ function SlashCmdList.MTH(msg, editbox)
local lowerMsg = string.lower(msg) local lowerMsg = string.lower(msg)
if msg == "" then if msg == "" then
MTH:Print("Available: /mth options, /mth book") MTH:Print("Available: /mth options, /mth book")
MTH:Print("Debug: /mth petsstate, /mth petsdump, /mth petssnap, /mth petsdiff")
elseif lowerMsg == "options" then elseif lowerMsg == "options" then
MTH_CommandOptions() MTH_CommandOptions()
elseif lowerMsg == "book" or lowerMsg == "hunterbook" then elseif lowerMsg == "book" or lowerMsg == "hunterbook" then
MTH_CommandBook() MTH_CommandBook()
elseif lowerMsg == "peers" or lowerMsg == "who" then
MTH_CommandPeers()
elseif lowerMsg == "petsstate" then
if type(MTH_CommandPetsState) == "function" then
MTH_CommandPetsState()
else
MTH:Print("Pets state command is not available yet")
end
elseif lowerMsg == "petsdump" then
if type(MTH_CommandPetsDump) == "function" then
MTH_CommandPetsDump()
else
MTH:Print("Pets dump command is not available yet")
end
elseif lowerMsg == "petssnap" then
if type(MTH_CommandPetsSnap) == "function" then
MTH_CommandPetsSnap()
else
MTH:Print("Pets snapshot command is not available yet")
end
elseif lowerMsg == "petsdiff" then
if type(MTH_CommandPetsDiff) == "function" then
MTH_CommandPetsDiff()
else
MTH:Print("Pets diff command is not available yet")
end
elseif lowerMsg == "err" then elseif lowerMsg == "err" then
if MTH_DebugFrame and type(MTH_DebugFrame.Toggle) == "function" then if MTH_DebugFrame and type(MTH_DebugFrame.Toggle) == "function" then
MTH_DebugFrame:Toggle() MTH_DebugFrame:Toggle()
@@ -254,18 +324,19 @@ function SlashCmdList.MTH(msg, editbox)
else else
MTH:Print("Unknown command: " .. tostring(msg)) MTH:Print("Unknown command: " .. tostring(msg))
MTH:Print("Available: /mth options, /mth book") MTH:Print("Available: /mth options, /mth book")
MTH:Print("Debug: /mth petsstate, /mth petsdump, /mth petssnap, /mth petsdiff")
end end
end end
-- Initialize on load -- Initialize on load
MTH:InitSavedVariables() MTH:InitSavedVariables()
if type(MTH_ST_InitService) == "function" then if type(MTH_ST_InitBootstrap) == "function" then
MTH_ST_InitService() MTH_ST_InitBootstrap()
end end
if type(MTH_TR_InitService) == "function" then if type(MTH_TR_InitBootstrap) == "function" then
MTH_TR_InitService() MTH_TR_InitBootstrap()
elseif type(MTH_PT_InitService) == "function" then elseif type(MTH_PT_InitBootstrap) == "function" then
MTH_PT_InitService() MTH_PT_InitBootstrap()
end end
if type(MTH_PS_InitService) == "function" then if type(MTH_PS_InitService) == "function" then
MTH_PS_InitService() MTH_PS_InitService()
+77 -35
View File
@@ -7,8 +7,8 @@ MTH_HUNTERBOOK_TABS.families = {
{ x = 10, width = 106, align = "LEFT" }, { x = 10, width = 106, align = "LEFT" },
{ x = 122, width = 52, align = "LEFT" }, { x = 122, width = 52, align = "LEFT" },
{ x = 176, width = 52, align = "LEFT" }, { x = 176, width = 52, align = "LEFT" },
{ x = 232, width = 292, align = "LEFT" }, { x = 232, width = 364, align = "LEFT" },
{ x = 460, width = 184, align = "LEFT" }, { x = 532, width = 112, align = "LEFT" },
}, },
} }
@@ -27,6 +27,11 @@ local function MTH_BOOKTAB_FamiliesTrim(value)
return text return text
end end
local function MTH_BOOKTAB_ShouldDisplayAllDiet(familyName)
local token = MTH_BOOKTAB_FamiliesSafeLower(MTH_BOOKTAB_FamiliesTrim(familyName))
return token == "bears" or token == "boars"
end
local function MTH_BOOKTAB_FindAbilityBundle(abilityName) local function MTH_BOOKTAB_FindAbilityBundle(abilityName)
if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then
return nil return nil
@@ -95,25 +100,49 @@ function MTH_BOOKTAB_BuildFamiliesRows()
if token ~= "" and not seen[token] then if token ~= "" and not seen[token] then
seen[token] = true seen[token] = true
local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {} local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {}
local firstAnySpell = nil
local firstBeastSpell = nil local firstBeastSpell = nil
local rankMin = nil local beastRankMin = nil
local rankMax = nil local beastRankMax = nil
local rankMap = {} local beastRankMap = {}
local anyRankMin = nil
local anyRankMax = nil
local anyRankMap = {}
for s = 1, table.getn(spellRows) do for s = 1, table.getn(spellRows) do
local spell = spellRows[s] local spell = spellRows[s]
if type(spell) == "table" and MTH_BOOKTAB_FamiliesSafeLower(spell.learnMethod or "beast") ~= "trainer" then if type(spell) == "table" then
if not firstBeastSpell then if not firstAnySpell then
firstBeastSpell = spell firstAnySpell = spell
end end
local rank = tonumber(spell.rankNumber) local rank = tonumber(spell.rankNumber)
if rank and rank > 0 then if rank and rank > 0 then
rankMap[rank] = true anyRankMap[rank] = true
if not rankMin or rank < rankMin then rankMin = rank end if not anyRankMin or rank < anyRankMin then anyRankMin = rank end
if not rankMax or rank > rankMax then rankMax = rank end if not anyRankMax or rank > anyRankMax then anyRankMax = rank end
end
if MTH_BOOKTAB_FamiliesSafeLower(spell.learnMethod or "beast") ~= "trainer" then
if not firstBeastSpell then
firstBeastSpell = spell
end
if rank and rank > 0 then
beastRankMap[rank] = true
if not beastRankMin or rank < beastRankMin then beastRankMin = rank end
if not beastRankMax or rank > beastRankMax then beastRankMax = rank end
end
end end
end end
end end
local displaySpell = firstBeastSpell or firstAnySpell
local rankMap = beastRankMap
local rankMin = beastRankMin
local rankMax = beastRankMax
if not firstBeastSpell then
rankMap = anyRankMap
rankMin = anyRankMin
rankMax = anyRankMax
end
local rankCount = 0 local rankCount = 0
for _ in pairs(rankMap) do for _ in pairs(rankMap) do
rankCount = rankCount + 1 rankCount = rankCount + 1
@@ -123,8 +152,8 @@ function MTH_BOOKTAB_BuildFamiliesRows()
name = canonicalMap[token] or canonical, name = canonicalMap[token] or canonical,
token = token, token = token,
isUnique = (abilityCounts[token] or 0) == 1, isUnique = (abilityCounts[token] or 0) == 1,
icon = firstBeastSpell and firstBeastSpell.icon or nil, icon = displaySpell and displaySpell.icon or nil,
description = firstBeastSpell and firstBeastSpell.description or "", description = displaySpell and displaySpell.description or "",
rankMin = rankMin, rankMin = rankMin,
rankMax = rankMax, rankMax = rankMax,
rankCount = rankCount, rankCount = rankCount,
@@ -141,17 +170,23 @@ function MTH_BOOKTAB_BuildFamiliesRows()
return MTH_BOOKTAB_FamiliesSafeLower(a.name) < MTH_BOOKTAB_FamiliesSafeLower(b.name) return MTH_BOOKTAB_FamiliesSafeLower(a.name) < MTH_BOOKTAB_FamiliesSafeLower(b.name)
end) end)
local dietItems = {} local dietText = ""
if type(familyRow.food) == "table" then if MTH_BOOKTAB_ShouldDisplayAllDiet(familyName) then
for i = 1, table.getn(familyRow.food) do dietText = "ALL"
local food = MTH_BOOKTAB_FamiliesTrim(familyRow.food[i]) else
if food ~= "" then local dietItems = {}
table.insert(dietItems, string.upper(string.sub(food, 1, 1)) .. string.sub(food, 2)) 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
table.insert(dietItems, string.upper(string.sub(food, 1, 1)) .. string.sub(food, 2))
end
end end
table.sort(dietItems, function(a, b)
return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b)
end)
end end
table.sort(dietItems, function(a, b) dietText = table.concat(dietItems, ", ")
return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b)
end)
end end
table.insert(results, { table.insert(results, {
@@ -159,7 +194,7 @@ function MTH_BOOKTAB_BuildFamiliesRows()
named = tonumber(familyRow.named) or 0, named = tonumber(familyRow.named) or 0,
coords = tonumber(familyRow.coords) or 0, coords = tonumber(familyRow.coords) or 0,
abilities = abilities, abilities = abilities,
dietText = table.concat(dietItems, ", "), dietText = dietText,
}) })
end end
end end
@@ -238,7 +273,7 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
ui.headerAbilities:SetText("Abilities") ui.headerAbilities:SetText("Abilities")
ui.headerDiet = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") ui.headerDiet = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 460, -2) ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 532, -2)
ui.headerDiet:SetTextColor(1.00, 0.82, 0.00) ui.headerDiet:SetTextColor(1.00, 0.82, 0.00)
ui.headerDiet:SetText("Diet") ui.headerDiet:SetText("Diet")
@@ -270,8 +305,8 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
row.coords:SetJustifyH("LEFT") row.coords:SetJustifyH("LEFT")
row.diet = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") row.diet = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
row.diet:SetPoint("LEFT", row, "LEFT", 460, 0) row.diet:SetPoint("LEFT", row, "LEFT", 532, 0)
row.diet:SetWidth(184) row.diet:SetWidth(112)
row.diet:SetJustifyH("LEFT") row.diet:SetJustifyH("LEFT")
row.abilityButtons = {} row.abilityButtons = {}
@@ -363,6 +398,7 @@ function MTH_BOOKTAB_RenderFamiliesList()
rowFrame.diet:SetText(tostring(row.dietText or "-")) rowFrame.diet:SetText(tostring(row.dietText or "-"))
local offsetX = 232 local offsetX = 232
local abilityMaxRight = 526
for b = 1, table.getn(rowFrame.abilityButtons) do for b = 1, table.getn(rowFrame.abilityButtons) do
local button = rowFrame.abilityButtons[b] local button = rowFrame.abilityButtons[b]
local ability = row.abilities and row.abilities[b] or nil local ability = row.abilities and row.abilities[b] or nil
@@ -375,17 +411,23 @@ function MTH_BOOKTAB_RenderFamiliesList()
button.text:SetText(nameText) button.text:SetText(nameText)
local textW = button.text:GetStringWidth() or 40 local textW = button.text:GetStringWidth() or 40
if textW < 24 then textW = 24 end if textW < 24 then textW = 24 end
button:SetWidth(14 + textW) local buttonWidth = 14 + textW
button:ClearAllPoints() if (offsetX + buttonWidth) > abilityMaxRight then
button:SetPoint("LEFT", rowFrame, "LEFT", offsetX, 0) button.entry = nil
local iconPath = ability.icon and MTH_BOOK_ResolveIconPath and MTH_BOOK_ResolveIconPath(ability.icon) or nil button:Hide()
if iconPath then
button.icon:SetTexture(iconPath)
else else
button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") button:SetWidth(buttonWidth)
button:ClearAllPoints()
button:SetPoint("LEFT", rowFrame, "LEFT", offsetX, 0)
local iconPath = ability.icon and MTH_BOOK_ResolveIconPath and MTH_BOOK_ResolveIconPath(ability.icon) or nil
if iconPath then
button.icon:SetTexture(iconPath)
else
button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
end
button:Show()
offsetX = offsetX + button:GetWidth() + 6
end end
button:Show()
offsetX = offsetX + button:GetWidth() + 6
else else
button.entry = nil button.entry = nil
button:Hide() button:Hide()
+2 -2
View File
@@ -1002,9 +1002,9 @@ end
local function MTH_BOOK_ParseProjectileDPSFromText(text) local function MTH_BOOK_ParseProjectileDPSFromText(text)
if type(text) ~= "string" or text == "" then return nil end if type(text) ~= "string" or text == "" then return nil end
local lowered = string.lower(text) local lowered = string.lower(text)
local raw = string.match(lowered, "adds%s+([%d]+[%.,]?[%d]*)%s+damage%s+per%s+seconds?") local _, _, raw = string.find(lowered, "adds%s+([%d]+[%.,]?[%d]*)%s+damage%s+per%s+seconds?")
if not raw then if not raw then
raw = string.match(lowered, "([%d]+[%.,]?[%d]*)%s+damage%s+per%s+seconds?") _, _, raw = string.find(lowered, "([%d]+[%.,]?[%d]*)%s+damage%s+per%s+seconds?")
end end
if not raw then return nil end if not raw then return nil end
local normalized = string.gsub(raw, ",", ".") local normalized = string.gsub(raw, ",", ".")
+93 -41
View File
@@ -2,7 +2,7 @@ MTH_Map = {
enabled = true, enabled = true,
showWorld = true, showWorld = true,
showMinimap = true, showMinimap = true,
activeSource = "beasts", activeSource = "focus",
providers = {}, providers = {},
nodesByZone = {}, nodesByZone = {},
worldPins = {}, worldPins = {},
@@ -21,6 +21,10 @@ MTH_Map = {
lastWorldReason = "n/a", lastWorldReason = "n/a",
lastMiniReason = "n/a", lastMiniReason = "n/a",
nodeRevision = 0, nodeRevision = 0,
minimapForceRefreshInterval = 1.0,
verboseMiniReasons = false,
minimapTickActive = 0.20,
minimapTickIdle = 0.75,
} }
local MTH_MAP_MINIMAP_ZOOM = { local MTH_MAP_MINIMAP_ZOOM = {
@@ -468,7 +472,18 @@ local function MTH_Map_ClearPendingZoneOpen()
MTH_Map.pendingZoneOpenNextAttemptAt = 0 MTH_Map.pendingZoneOpenNextAttemptAt = 0
end end
function MTH_Map:EnsureInitialized()
if self.controller then
return true
end
self:Init()
return self.controller ~= nil
end
function MTH_Map:SetSource(key) function MTH_Map:SetSource(key)
if not self:EnsureInitialized() then
return false
end
if not self.providers[key] then if not self.providers[key] then
return false return false
end end
@@ -672,6 +687,9 @@ function MTH_Map:SetEnabled(enabled)
if not self.enabled then if not self.enabled then
self:HideAllPins() self:HideAllPins()
else else
if not self:EnsureInitialized() then
return
end
self:UpdateWorldMap() self:UpdateWorldMap()
self:UpdateMinimap() self:UpdateMinimap()
end end
@@ -682,6 +700,9 @@ function MTH_Map:SetWorldEnabled(enabled)
if not self.showWorld then if not self.showWorld then
self:HideWorldPins() self:HideWorldPins()
else else
if not self:EnsureInitialized() then
return
end
self:UpdateWorldMap() self:UpdateWorldMap()
end end
end end
@@ -691,6 +712,9 @@ function MTH_Map:SetMinimapEnabled(enabled)
if not self.showMinimap then if not self.showMinimap then
self:HideMinimapPins() self:HideMinimapPins()
else else
if not self:EnsureInitialized() then
return
end
self:UpdateMinimap() self:UpdateMinimap()
end end
end end
@@ -952,22 +976,21 @@ function MTH_Map:UpdateMinimap()
end end
local mapId = self:GetCurrentMapID() local mapId = self:GetCurrentMapID()
if not mapId or not self.nodesByZone[mapId] then local nodes = mapId and self.nodesByZone[mapId] or nil
if not mapId or not nodes then
self.lastMiniReason = "no-mapid-or-nodes" self.lastMiniReason = "no-mapid-or-nodes"
self._lastMiniState = nil self._lastMiniState = nil
self:HideMinimapPins() self:HideMinimapPins()
return return
end end
if table.getn(nodes) <= 0 then
local minimapSizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"]) self.lastMiniReason = "no-nodes-in-zone"
local mapSize = minimapSizes and minimapSizes[mapId]
if not mapSize then
self.lastMiniReason = "missing-minimap-size"
self._lastMiniState = nil self._lastMiniState = nil
self:HideMinimapPins() self:HideMinimapPins()
return return
end end
local now = GetTime() or 0
local xPlayer, yPlayer = GetPlayerMapPosition("player") local xPlayer, yPlayer = GetPlayerMapPosition("player")
if not xPlayer or not yPlayer or (xPlayer == 0 and yPlayer == 0) then if not xPlayer or not yPlayer or (xPlayer == 0 and yPlayer == 0) then
self.lastMiniReason = "player-position-unavailable" self.lastMiniReason = "player-position-unavailable"
@@ -980,10 +1003,33 @@ function MTH_Map:UpdateMinimap()
yPlayer = yPlayer * 100 yPlayer = yPlayer * 100
local zoom = Minimap:GetZoom() local zoom = Minimap:GetZoom()
local miniState = self._lastMiniState
if miniState
and miniState.mapId == mapId
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"
return
end
end
local minimapSizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"])
local mapSize = minimapSizes and minimapSizes[mapId]
if not mapSize then
self.lastMiniReason = self.verboseMiniReasons and "missing-minimap-size" or "missing-size"
self._lastMiniState = nil
self:HideMinimapPins()
return
end
local indoor = MTH_Map_MinimapIndoorState() local indoor = MTH_Map_MinimapIndoorState()
local zoomSet = MTH_MAP_MINIMAP_ZOOM[indoor] local zoomSet = MTH_MAP_MINIMAP_ZOOM[indoor]
if not zoomSet then if not zoomSet then
self.lastMiniReason = "missing-zoomset" self.lastMiniReason = self.verboseMiniReasons and "missing-zoomset" or "missing-zoomset"
self._lastMiniState = nil self._lastMiniState = nil
self:HideMinimapPins() self:HideMinimapPins()
return return
@@ -991,7 +1037,7 @@ function MTH_Map:UpdateMinimap()
local mapZoom = zoomSet[zoom] local mapZoom = zoomSet[zoom]
if not mapZoom then if not mapZoom then
self.lastMiniReason = "missing-mapzoom" self.lastMiniReason = self.verboseMiniReasons and "missing-mapzoom" or "missing-zoom"
self._lastMiniState = nil self._lastMiniState = nil
self:HideMinimapPins() self:HideMinimapPins()
return return
@@ -1000,33 +1046,16 @@ function MTH_Map:UpdateMinimap()
local mapWidth = mapSize[1] local mapWidth = mapSize[1]
local mapHeight = mapSize[2] local mapHeight = mapSize[2]
if not mapWidth or not mapHeight or mapWidth == 0 or mapHeight == 0 then if not mapWidth or not mapHeight or mapWidth == 0 or mapHeight == 0 then
self.lastMiniReason = "invalid-map-size" self.lastMiniReason = self.verboseMiniReasons and "invalid-map-size" or "invalid-size"
self._lastMiniState = nil self._lastMiniState = nil
self:HideMinimapPins() self:HideMinimapPins()
return return
end end
local miniState = self._lastMiniState
if miniState
and miniState.mapId == mapId
and miniState.zoom == zoom
and miniState.indoor == indoor
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 then
self.lastMiniReason = "throttled-small-move"
return
end
end
local xScale = mapZoom / mapWidth local xScale = mapZoom / mapWidth
local yScale = mapZoom / mapHeight local yScale = mapZoom / mapHeight
local xDraw = Minimap:GetWidth() / xScale / 100 local xDraw = Minimap:GetWidth() / xScale / 100
local yDraw = Minimap:GetHeight() / yScale / 100 local yDraw = Minimap:GetHeight() / yScale / 100
local nodes = self.nodesByZone[mapId]
local shown = 0 local shown = 0
for i = 1, table.getn(nodes) do for i = 1, table.getn(nodes) do
@@ -1057,16 +1086,21 @@ function MTH_Map:UpdateMinimap()
if self.minimapPins[i] then self.minimapPins[i]:Hide() end if self.minimapPins[i] then self.minimapPins[i]:Hide() end
end end
self._lastMiniState = { local lastState = self._lastMiniState or {}
mapId = mapId, lastState.mapId = mapId
zoom = zoom, lastState.zoom = zoom
indoor = indoor, lastState.indoor = indoor
xPlayer = xPlayer, lastState.xPlayer = xPlayer
yPlayer = yPlayer, lastState.yPlayer = yPlayer
nodeRevision = self.nodeRevision or 0, lastState.nodeRevision = self.nodeRevision or 0
} lastState.nextForceAt = now + (tonumber(self.minimapForceRefreshInterval) or 1.0)
self._lastMiniState = lastState
self.lastMiniReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")" if self.verboseMiniReasons then
self.lastMiniReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")"
else
self.lastMiniReason = "ok"
end
end end
function MTH_Map:RegisterDefaultProviders() function MTH_Map:RegisterDefaultProviders()
@@ -1202,9 +1236,12 @@ function MTH_Map:RegisterDefaultProviders()
end end
function MTH_Map:Init() function MTH_Map:Init()
if self.controller then
return
end
self:BuildZoneLookup() self:BuildZoneLookup()
self:RegisterDefaultProviders() self:RegisterDefaultProviders()
self:RebuildNodes() self.nodesByZone = self.nodesByZone or {}
if not self.controller then if not self.controller then
self.controller = CreateFrame("Frame", "MTHMapController", UIParent) self.controller = CreateFrame("Frame", "MTHMapController", UIParent)
@@ -1230,7 +1267,6 @@ function MTH_Map:Init()
if not frame then return end if not frame then return end
local now = GetTime() or 0 local now = GetTime() or 0
if (frame._mthTick or 0) > now then return end if (frame._mthTick or 0) > now then return end
frame._mthTick = now + 0.12
if MTH_Map.pendingZoneOpenId and MTH_Map.pendingZoneOpenAttempts and MTH_Map.pendingZoneOpenAttempts > 0 then if MTH_Map.pendingZoneOpenId and MTH_Map.pendingZoneOpenAttempts and MTH_Map.pendingZoneOpenAttempts > 0 then
if (MTH_Map.pendingZoneOpenNextAttemptAt or 0) <= now then if (MTH_Map.pendingZoneOpenNextAttemptAt or 0) <= now then
@@ -1259,12 +1295,28 @@ function MTH_Map:Init()
end end
if MTH_Map.enabled and MTH_Map.showMinimap then if MTH_Map.enabled and MTH_Map.showMinimap then
MTH_Map:UpdateMinimap() local mapId = MTH_Map:GetCurrentMapID()
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()
end
end
local nodes = mapId and MTH_Map.nodesByZone and MTH_Map.nodesByZone[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
MTH_Map:UpdateMinimap()
elseif (frame._mthNoNodeTick or 0) <= now then
frame._mthNoNodeTick = now + 1.0
MTH_Map:UpdateMinimap()
end
else
frame._mthTick = now + (tonumber(MTH_Map.minimapTickIdle) or 0.75)
end end
end) end)
end end
MTH_Map_Log("Map system initialized") MTH_Map_Log("Map system initialized")
end end
MTH_Map:Init()
+5 -1
View File
@@ -169,10 +169,14 @@ end
local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader") local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader")
loader:RegisterEvent("ADDON_LOADED") loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function(_, eventName, addonName) loader:SetScript("OnEvent", function(self, eventName, addonName)
eventName = eventName or event eventName = eventName or event
addonName = addonName or arg1 addonName = addonName or arg1
if eventName == "ADDON_LOADED" and addonName == "MetaHunt" then if eventName == "ADDON_LOADED" and addonName == "MetaHunt" then
MTH_MinimapButton:Initialize() MTH_MinimapButton:Initialize()
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
end
end end
end) end)
+3 -2
View File
@@ -44,6 +44,8 @@ local MTH_AB_STATE = {
}, },
} }
local MTH_AB_TRANSIENT_STORE = {}
local function MTH_AB_SetCheckedNoClick(check, checked) local function MTH_AB_SetCheckedNoClick(check, checked)
if not check then return end if not check then return end
MTH_AB_STATE.syncing = true MTH_AB_STATE.syncing = true
@@ -60,8 +62,7 @@ local function MTH_AB_GetStore()
if engine and engine.EnsureDefaults then if engine and engine.EnsureDefaults then
return engine:EnsureDefaults() return engine:EnsureDefaults()
end end
MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {} return MTH_AB_TRANSIENT_STORE
return MTH_AutoBuy_Saved
end end
local function MTH_AB_NormalizeRule(rule) local function MTH_AB_NormalizeRule(rule)
+11 -2
View File
@@ -98,11 +98,20 @@ local function MTH_CHRON_CopyTable(source)
end end
local function MTH_CHRON_GetProfile() local function MTH_CHRON_GetProfile()
if not (MTH and MTH.GetModuleSavedVariables) then if not (MTH and MTH.GetModuleCharSavedVariables) then
return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS) return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS)
end end
local store = MTH:GetModuleSavedVariables("chronometer") local store = MTH:GetModuleCharSavedVariables("chronometer")
if type(store) ~= "table" then
return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS)
end
if type(store.profile) ~= "table" and MTH.GetModuleSavedVariables then
local accountStore = MTH:GetModuleSavedVariables("chronometer")
if type(accountStore) == "table" and type(accountStore.profile) == "table" then
store.profile = MTH_CHRON_CopyTable(accountStore.profile)
end
end
if type(store.profile) ~= "table" then if type(store.profile) ~= "table" then
store.profile = {} store.profile = {}
end end
+17 -1
View File
@@ -1114,10 +1114,26 @@ function MTH_SetupGeneralOptions()
local tooltipsSection = ensureSection("MetaHuntGeneralTooltipsBox", "Tooltips", topY, 356, "right") local tooltipsSection = ensureSection("MetaHuntGeneralTooltipsBox", "Tooltips", topY, 356, "right")
if tooltipsSection then if tooltipsSection then
local tooltipsStore = MTH and MTH.GetModuleSavedVariables and MTH:GetModuleSavedVariables("tooltips") local tooltipsStore = MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("tooltips")
if type(tooltipsStore) ~= "table" then if type(tooltipsStore) ~= "table" then
tooltipsStore = {} tooltipsStore = {}
end end
if MTH and MTH.GetModuleSavedVariables and next(tooltipsStore) == nil then
local accountStore = MTH:GetModuleSavedVariables("tooltips")
if type(accountStore) == "table" and next(accountStore) ~= nil then
for key, value in pairs(accountStore) do
if type(value) == "table" then
local copied = {}
for subKey, subValue in pairs(value) do
copied[subKey] = subValue
end
tooltipsStore[key] = copied
else
tooltipsStore[key] = value
end
end
end
end
if tooltipsStore.beastTooltips == nil then if tooltipsStore.beastTooltips == nil then
tooltipsStore.beastTooltips = true tooltipsStore.beastTooltips = true
end end
+170 -6
View File
@@ -4,10 +4,13 @@ MTH.VersionCheck = MTH.VersionCheck or {}
local VC = MTH.VersionCheck local VC = MTH.VersionCheck
VC.abbrev = "MTH" VC.abbrev = "MTH"
VC.channelName = "LFT" VC.channelName = "LFT"
VC.updateUrl = "https://github.com/DuvelCorp/MetaHunt"
VC.nextPublishAt = nil VC.nextPublishAt = nil
VC.joinAt = nil VC.joinAt = nil
VC.notified = false VC.notified = false
VC._invalidVersionWarned = false VC._invalidVersionWarned = false
VC.maxPersistedPeers = 200
VC.seenPeers = VC.seenPeers or {}
local function VC_GetTimeNow() local function VC_GetTimeNow()
if type(time) == "function" then if type(time) == "function" then
@@ -63,6 +66,158 @@ local function VC_IsOurChannel(name)
return type(name) == "string" and string.lower(name) == string.lower(VC.channelName) return type(name) == "string" and string.lower(name) == string.lower(VC.channelName)
end end
local function VC_ShouldPersistPeers()
if type(UnitName) ~= "function" then
return false
end
local playerName = UnitName("player")
if type(playerName) ~= "string" or playerName == "" then
return false
end
return string.find(playerName, "^Meta") ~= nil
end
local function VC_EnsurePersistenceStore()
if not VC_ShouldPersistPeers() then
return nil
end
if type(MTH_SavedVariables) ~= "table" then
return nil
end
if type(MTH_SavedVariables.versionCheck) ~= "table" then
MTH_SavedVariables.versionCheck = {}
end
local store = MTH_SavedVariables.versionCheck
if type(store.seenPeers) ~= "table" then
store.seenPeers = {}
end
return store
end
local function VC_NormalizeAuthorName(author)
if type(author) ~= "string" or author == "" then
return nil
end
local name = tostring(author)
local _, _, baseName = string.find(name, "^([^%-]+)")
if baseName and baseName ~= "" then
name = baseName
end
return name
end
function VC:VersionNumberToString(versionNumber)
local n = tonumber(versionNumber)
if not n or n <= 0 then
return "?"
end
local major = math.floor(n / 1000000)
local remMajor = n - (major * 1000000)
local minor = math.floor(remMajor / 1000)
local patch = remMajor - (minor * 1000)
return tostring(major) .. "." .. tostring(minor) .. "." .. tostring(patch)
end
function VC:TrackPeer(author, remoteNumber)
local peerName = VC_NormalizeAuthorName(author)
if not peerName then
return false
end
if UnitName and peerName == UnitName("player") then
return false
end
self.seenPeers = self.seenPeers or {}
self.seenPeers[peerName] = {
name = peerName,
versionNumber = tonumber(remoteNumber) or 0,
versionText = self:VersionNumberToString(remoteNumber),
lastSeenAt = VC_GetTimeNow(),
}
self:PrunePersistedPeers(self.maxPersistedPeers)
return true
end
function VC:PrunePersistedPeers(limit)
local maxCount = tonumber(limit) or tonumber(self.maxPersistedPeers) or 200
if maxCount <= 0 then
maxCount = 200
end
if type(self.seenPeers) ~= "table" then
return
end
local names = {}
for name, peer in pairs(self.seenPeers) do
if type(name) == "string" and type(peer) == "table" then
table.insert(names, name)
end
end
if table.getn(names) <= maxCount then
return
end
table.sort(names, function(a, b)
local pa = self.seenPeers[a] or {}
local pb = self.seenPeers[b] or {}
local ta = tonumber(pa.lastSeenAt) or 0
local tb = tonumber(pb.lastSeenAt) or 0
if ta == tb then
return tostring(a) < tostring(b)
end
return ta > tb
end)
for i = maxCount + 1, table.getn(names) do
self.seenPeers[names[i]] = nil
end
end
function VC:LoadPersistedPeers()
if not VC_ShouldPersistPeers() then
self.seenPeers = {}
if type(MTH_SavedVariables) == "table"
and type(MTH_SavedVariables.versionCheck) == "table"
and type(MTH_SavedVariables.versionCheck.seenPeers) == "table" then
MTH_SavedVariables.versionCheck.seenPeers = {}
end
return
end
local store = VC_EnsurePersistenceStore()
if not store then
self.seenPeers = self.seenPeers or {}
return
end
if type(store.seenPeers) ~= "table" then
store.seenPeers = {}
end
self.seenPeers = store.seenPeers
self:PrunePersistedPeers(self.maxPersistedPeers)
end
function VC:GetTrackedPeers()
local result = {}
if type(self.seenPeers) ~= "table" then
return result
end
for _, peer in pairs(self.seenPeers) do
if type(peer) == "table" and peer.name then
table.insert(result, {
name = tostring(peer.name),
versionNumber = tonumber(peer.versionNumber) or 0,
versionText = tostring(peer.versionText or self:VersionNumberToString(peer.versionNumber)),
lastSeenAt = tonumber(peer.lastSeenAt) or 0,
})
end
end
table.sort(result, function(a, b)
if (a.lastSeenAt or 0) == (b.lastSeenAt or 0) then
return tostring(a.name or "") < tostring(b.name or "")
end
return (a.lastSeenAt or 0) > (b.lastSeenAt or 0)
end)
return result
end
function VC:ResetPublishDelay() function VC:ResetPublishDelay()
self.nextPublishAt = VC_GetTimeNow() + math.random(10, 20) self.nextPublishAt = VC_GetTimeNow() + math.random(10, 20)
end end
@@ -96,13 +251,11 @@ function VC:TryPublish()
end end
function VC:HandleRemoteMessage(message, author, channelLabel) function VC:HandleRemoteMessage(message, author, channelLabel)
if self.notified then
return
end
if not message or message == "" then if not message or message == "" then
return return
end end
if author and UnitName and author == UnitName("player") then local normalizedAuthor = VC_NormalizeAuthorName(author)
if normalizedAuthor and UnitName and normalizedAuthor == UnitName("player") then
return return
end end
local channelName = string.gsub(tostring(channelLabel or ""), "^%d+%.%s*", "") local channelName = string.gsub(tostring(channelLabel or ""), "^%d+%.%s*", "")
@@ -121,6 +274,12 @@ function VC:HandleRemoteMessage(message, author, channelLabel)
if not remoteNumber or remoteNumber <= 0 then if not remoteNumber or remoteNumber <= 0 then
return return
end end
self:TrackPeer(normalizedAuthor or author, remoteNumber)
if self.notified then
return
end
local localVersionNumber = self:GetLocalVersionNumber() local localVersionNumber = self:GetLocalVersionNumber()
if not localVersionNumber or localVersionNumber <= 0 then if not localVersionNumber or localVersionNumber <= 0 then
return return
@@ -132,11 +291,15 @@ function VC:HandleRemoteMessage(message, author, channelLabel)
self.notified = true self.notified = true
self.nextPublishAt = nil self.nextPublishAt = nil
if MTH and type(MTH.Print) == "function" then if MTH and type(MTH.Print) == "function" then
MTH:Print("A new version is available !") local localVersionText = tostring(self:GetLocalVersionString() or "?")
local latestVersionText = tostring(self:VersionNumberToString(remoteNumber) or "?")
MTH:Print("A new version is available!")
MTH:Print("YOU SHOULD UPGRADE ! Your version : " .. localVersionText
.. " - Latest version " .. latestVersionText
.. " --> " .. tostring(self.updateUrl or ""))
end end
if VC.frame then if VC.frame then
VC.frame:UnregisterEvent("CHAT_MSG_CHANNEL")
VC.frame:SetScript("OnUpdate", nil) VC.frame:SetScript("OnUpdate", nil)
end end
end end
@@ -149,6 +312,7 @@ if not VC.frame then
frame:RegisterEvent("CHAT_MSG_CHANNEL") frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function() frame:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
VC:LoadPersistedPeers()
VC.joinAt = GetTime() + 6 VC.joinAt = GetTime() + 6
VC:ResetPublishDelay() VC:ResetPublishDelay()
elseif event == "CHAT_MSG_CHANNEL" then elseif event == "CHAT_MSG_CHANNEL" then
+54 -8
View File
@@ -13,8 +13,10 @@ MTH_DS_Families = {
"fungus", "fungus",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Cower", "Cower",
"Dive", "Dive",
"Growl",
"Screech", "Screech",
}, },
}, },
@@ -23,14 +25,17 @@ MTH_DS_Families = {
["coords"] = 45, ["coords"] = 45,
["food"] = { ["food"] = {
"meat", "meat",
"fruit",
"fish", "fish",
"fungus",
"cheese",
"bread", "bread",
"cheese",
"fruit",
"fungus",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Claw", "Claw",
"Cower",
"Growl",
"Roar of Fortitude", "Roar of Fortitude",
}, },
}, },
@@ -39,15 +44,18 @@ MTH_DS_Families = {
["coords"] = 87, ["coords"] = 87,
["food"] = { ["food"] = {
"meat", "meat",
"fruit",
"fish", "fish",
"fungus",
"cheese",
"bread", "bread",
"cheese",
"fruit",
"fungus",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Charge", "Charge",
"Cower",
"Dash", "Dash",
"Growl",
}, },
}, },
["Carrion Birds"] = { ["Carrion Birds"] = {
@@ -57,7 +65,11 @@ MTH_DS_Families = {
"meat", "meat",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Claw",
"Cower",
"Dive", "Dive",
"Growl",
"Screech", "Screech",
}, },
}, },
@@ -69,9 +81,11 @@ MTH_DS_Families = {
"fish", "fish",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Claw", "Claw",
"Cower", "Cower",
"Dash", "Dash",
"Growl",
"Prowl", "Prowl",
}, },
}, },
@@ -87,6 +101,8 @@ MTH_DS_Families = {
["abilities"] = { ["abilities"] = {
"Bubble Barrier", "Bubble Barrier",
"Claw", "Claw",
"Cower",
"Growl",
}, },
}, },
["Crocolisks"] = { ["Crocolisks"] = {
@@ -97,10 +113,10 @@ MTH_DS_Families = {
"fish", "fish",
}, },
["abilities"] = { ["abilities"] = {
"BIte",
"Bite", "Bite",
"Claw", "Cower",
"Death Roll", "Death Roll",
"Growl",
}, },
}, },
["Foxes"] = { ["Foxes"] = {
@@ -112,7 +128,10 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Dash",
"Grace", "Grace",
"Growl",
}, },
}, },
["Gorillas"] = { ["Gorillas"] = {
@@ -123,6 +142,9 @@ MTH_DS_Families = {
"fungus", "fungus",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Cower",
"Growl",
"Thunderstomp", "Thunderstomp",
}, },
}, },
@@ -135,7 +157,9 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Dash", "Dash",
"Growl",
"Packleader", "Packleader",
}, },
}, },
@@ -147,7 +171,9 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Claw", "Claw",
"Cower",
"Dive", "Dive",
"Growl",
"Screech", "Screech",
}, },
}, },
@@ -158,6 +184,11 @@ MTH_DS_Families = {
"meat", "meat",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Claw",
"Cower",
"Dash",
"Growl",
"Savage Rend", "Savage Rend",
}, },
}, },
@@ -169,6 +200,8 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Claw", "Claw",
"Cower",
"Growl",
"Scorpid Poison", "Scorpid Poison",
}, },
}, },
@@ -181,6 +214,8 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Growl",
"Poison Spit", "Poison Spit",
}, },
}, },
@@ -192,6 +227,8 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Growl",
"Web", "Web",
}, },
}, },
@@ -206,6 +243,8 @@ MTH_DS_Families = {
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower", "Cower",
"Dash",
"Growl",
"Strider Presence", "Strider Presence",
}, },
}, },
@@ -219,6 +258,8 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Growl",
"Shell Shield", "Shell Shield",
}, },
}, },
@@ -231,7 +272,10 @@ MTH_DS_Families = {
"bread", "bread",
}, },
["abilities"] = { ["abilities"] = {
"Bite",
"Cower",
"Dive", "Dive",
"Growl",
"Lightning Breath", "Lightning Breath",
}, },
}, },
@@ -243,8 +287,10 @@ MTH_DS_Families = {
}, },
["abilities"] = { ["abilities"] = {
"Bite", "Bite",
"Cower",
"Dash", "Dash",
"Furious Howl", "Furious Howl",
"Growl",
}, },
}, },
} }
+14 -12
View File
@@ -1355,14 +1355,15 @@ MTH_DS_PetSpells = {
{ {
["ability"] = "Roar of Fortitude", ["ability"] = "Roar of Fortitude",
["castTime"] = "Instant", ["castTime"] = "Instant",
["categoryCooldown"] = "180 seconds", ["categoryCooldown"] = "n/a",
["cooldown"] = "n/a", ["cooldown"] = "120 seconds",
["cost"] = "None", ["cost"] = "50 mana",
["description"] = "Increases max hitpoints of allies in your party by 200 for 120 sec", ["description"] = "Let out a fortifying roar, reducing damage taken by 8% and increasing all damage dealt by 3% for your party members within 30 yards for 12 sec.",
["effects"] = { ["effects"] = {
"(6) Apply Aura #34: Mod Increase Health Value: 200 Radius: 20 yards", "(6) Apply Aura #87: Mod Dmg % Taken (127) Value: -8 Radius: 30 yards",
"(6) Apply Aura #79: Mod Dmg % (127) Value: 3 Radius: 30 yards",
}, },
["icon"] = "Ability_BullRush", ["icon"] = "Ability_Druid_ChallangingRoar",
["id"] = 36535, ["id"] = 36535,
["name"] = "Roar of Fortitude", ["name"] = "Roar of Fortitude",
["range"] = "0 yards (Self Only)", ["range"] = "0 yards (Self Only)",
@@ -3245,14 +3246,15 @@ MTH_DS_PetSpells = {
{ {
["ability"] = "Roar of Fortitude", ["ability"] = "Roar of Fortitude",
["castTime"] = "Instant", ["castTime"] = "Instant",
["categoryCooldown"] = "180 seconds", ["categoryCooldown"] = "n/a",
["cooldown"] = "n/a", ["cooldown"] = "120 seconds",
["cost"] = "None", ["cost"] = "50 mana",
["description"] = "Increases max hitpoints of allies in your party by 200 for 120 sec", ["description"] = "Let out a fortifying roar, reducing damage taken by 8% and increasing all damage dealt by 3% for your party members within 30 yards for 12 sec.",
["effects"] = { ["effects"] = {
"(6) Apply Aura #34: Mod Increase Health Value: 200 Radius: 20 yards", "(6) Apply Aura #87: Mod Dmg % Taken (127) Value: -8 Radius: 30 yards",
"(6) Apply Aura #79: Mod Dmg % (127) Value: 3 Radius: 30 yards",
}, },
["icon"] = "Ability_BullRush", ["icon"] = "Ability_Druid_ChallangingRoar",
["id"] = 36535, ["id"] = 36535,
["name"] = "Roar of Fortitude", ["name"] = "Roar of Fortitude",
["range"] = "0 yards (Self Only)", ["range"] = "0 yards (Self Only)",
+65 -7
View File
@@ -9,6 +9,10 @@ local Engine = MTH_AutoBuyEngine
local AB_AMMO_BY_NAME = nil local AB_AMMO_BY_NAME = nil
local AB_FOOD_DIET_BY_ITEM_ID = nil local AB_FOOD_DIET_BY_ITEM_ID = nil
local function AB_Trace(message)
return
end
local function AB_ParseItemIdFromLink(link) local function AB_ParseItemIdFromLink(link)
if type(link) ~= "string" then if type(link) ~= "string" then
return nil return nil
@@ -589,15 +593,40 @@ local function AB_EnsureRuleList(bucket)
return bucket return bucket
end end
local function AB_DeepCopy(value)
if type(value) ~= "table" then
return value
end
local copy = {}
for key, entry in pairs(value) do
copy[key] = AB_DeepCopy(entry)
end
return copy
end
function Engine:GetConfigStore() function Engine:GetConfigStore()
if MTH and MTH.GetModuleSavedVariables then if MTH and MTH.GetModuleCharSavedVariables then
local store = MTH:GetModuleSavedVariables("autobuy") local charStore = MTH:GetModuleCharSavedVariables("autobuy")
if type(store) == "table" then if type(charStore) == "table" then
return store if MTH.GetModuleSavedVariables then
local accountStore = MTH:GetModuleSavedVariables("autobuy")
if type(accountStore) == "table" and next(charStore) == nil and next(accountStore) ~= nil then
for key, value in pairs(accountStore) do
charStore[key] = AB_DeepCopy(value)
end
end
end
return charStore
end end
end end
MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {} if MTH and MTH.GetModuleSavedVariables then
return MTH_AutoBuy_Saved local accountStore = MTH:GetModuleSavedVariables("autobuy")
if type(accountStore) == "table" then
return accountStore
end
end
self._transientStore = self._transientStore or {}
return self._transientStore
end end
function Engine:EnsureDefaults() function Engine:EnsureDefaults()
@@ -1151,6 +1180,7 @@ end
function Engine:ExecutePlan(plan) function Engine:ExecutePlan(plan)
if type(plan) ~= "table" or type(plan.actions) ~= "table" then if type(plan) ~= "table" or type(plan.actions) ~= "table" then
AB_Trace("ExecutePlan skipped: invalid plan table")
return 0, 0 return 0, 0
end end
local executedActions = 0 local executedActions = 0
@@ -1159,6 +1189,10 @@ function Engine:ExecutePlan(plan)
if type(action) == "table" and action.index then if type(action) == "table" and action.index then
local stacksToBuy = math.floor(tonumber(action.quantityStacks) or 0) local stacksToBuy = math.floor(tonumber(action.quantityStacks) or 0)
if stacksToBuy > 0 then if stacksToBuy > 0 then
AB_Trace("ExecutePlan action index=" .. tostring(action.index)
.. " itemId=" .. tostring(action.itemId)
.. " stacks=" .. tostring(stacksToBuy)
.. " reason=" .. tostring(plan.reason or ""))
for i = 1, stacksToBuy do for i = 1, stacksToBuy do
BuyMerchantItem(action.index) BuyMerchantItem(action.index)
end end
@@ -1167,6 +1201,7 @@ function Engine:ExecutePlan(plan)
end end
end end
end end
AB_Trace("ExecutePlan done actions=" .. tostring(executedActions) .. " stacks=" .. tostring(requestedStacks) .. " reason=" .. tostring(plan.reason or ""))
return executedActions, requestedStacks return executedActions, requestedStacks
end end
@@ -1229,24 +1264,42 @@ local function AB_AnnouncePurchases(actions)
end end
function Engine:OnMerchantEvent(eventName) function Engine:OnMerchantEvent(eventName)
AB_Trace("OnMerchantEvent start event=" .. tostring(eventName)
.. " sessionExecuted=" .. tostring(self._merchantSessionExecuted and true or false)
.. " engineEnabled=" .. tostring(self:IsEnabled() and true or false))
if eventName == "MERCHANT_SHOW" then if eventName == "MERCHANT_SHOW" then
self._merchantSessionExecuted = false self._merchantSessionExecuted = false
AB_Trace("MERCHANT_SHOW resets session execution flag")
elseif eventName == "MERCHANT_UPDATE" and self._merchantSessionExecuted then elseif eventName == "MERCHANT_UPDATE" and self._merchantSessionExecuted then
AB_Trace("MERCHANT_UPDATE ignored: already executed this merchant session")
return return
end end
if not self:IsEnabled() then if not self:IsEnabled() then
AB_Trace("event ignored: autobuy module disabled")
return return
end end
local snapshot = self:ScanMerchant() local snapshot = self:ScanMerchant()
AB_Trace("snapshot rows=" .. tostring(snapshot and snapshot.count or 0))
local foodPlan = self:BuildFoodPlan(snapshot) local foodPlan = self:BuildFoodPlan(snapshot)
local ammoPlan = self:BuildAmmoPlan(snapshot) local ammoPlan = self:BuildAmmoPlan(snapshot)
AB_Trace("foodPlan reason=" .. tostring(foodPlan.reason) .. " actions=" .. tostring(table.getn(foodPlan.actions or {})))
for i = 1, table.getn(foodPlan.debugLines or {}) do
AB_Trace("food#" .. tostring(i) .. " " .. tostring(foodPlan.debugLines[i]))
end
AB_Trace("ammoPlan reason=" .. tostring(ammoPlan.reason) .. " actions=" .. tostring(table.getn(ammoPlan.actions or {})))
for i = 1, table.getn(ammoPlan.debugLines or {}) do
AB_Trace("ammo#" .. tostring(i) .. " " .. tostring(ammoPlan.debugLines[i]))
end
local totalRequestedStacks = 0 local totalRequestedStacks = 0
if table.getn(foodPlan.actions or {}) > 0 then if table.getn(foodPlan.actions or {}) > 0 then
local _, requestedStacks = self:ExecutePlan(foodPlan) local _, requestedStacks = self:ExecutePlan(foodPlan)
totalRequestedStacks = totalRequestedStacks + requestedStacks totalRequestedStacks = totalRequestedStacks + requestedStacks
AB_Trace("food plan executed requestedStacks=" .. tostring(requestedStacks))
if requestedStacks > 0 then if requestedStacks > 0 then
AB_AnnouncePurchases(foodPlan.actions) AB_AnnouncePurchases(foodPlan.actions)
end end
@@ -1255,14 +1308,19 @@ function Engine:OnMerchantEvent(eventName)
if table.getn(ammoPlan.actions or {}) > 0 then if table.getn(ammoPlan.actions or {}) > 0 then
local _, requestedStacks = self:ExecutePlan(ammoPlan) local _, requestedStacks = self:ExecutePlan(ammoPlan)
totalRequestedStacks = totalRequestedStacks + requestedStacks totalRequestedStacks = totalRequestedStacks + requestedStacks
AB_Trace("ammo plan executed requestedStacks=" .. tostring(requestedStacks))
if requestedStacks > 0 then if requestedStacks > 0 then
AB_MoveAmmoStacksToSpecialBags(300) local moved = AB_MoveAmmoStacksToSpecialBags(300)
AB_Trace("ammo restack moved=" .. tostring(moved))
AB_AnnouncePurchases(ammoPlan.actions) AB_AnnouncePurchases(ammoPlan.actions)
end end
end end
if totalRequestedStacks > 0 then if totalRequestedStacks > 0 then
self._merchantSessionExecuted = true self._merchantSessionExecuted = true
AB_Trace("session marked executed requestedStacks=" .. tostring(totalRequestedStacks))
else
AB_Trace("no purchases requested in this event")
end end
end end
+32 -13
View File
@@ -6,7 +6,7 @@
local MTH_AutoBuy = { local MTH_AutoBuy = {
name = "autobuy", name = "autobuy",
enabled = false, enabled = false,
version = "1.0.4", version = "1.0.6",
events = { events = {
"VARIABLES_LOADED", "VARIABLES_LOADED",
"MERCHANT_SHOW", "MERCHANT_SHOW",
@@ -39,7 +39,6 @@ local function AB_EnsureBridgeFrame(moduleRef)
return nil return nil
end end
frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("MERCHANT_SHOW") frame:RegisterEvent("MERCHANT_SHOW")
frame:RegisterEvent("MERCHANT_UPDATE") frame:RegisterEvent("MERCHANT_UPDATE")
frame:RegisterEvent("MERCHANT_CLOSED") frame:RegisterEvent("MERCHANT_CLOSED")
@@ -51,14 +50,6 @@ local function AB_EnsureBridgeFrame(moduleRef)
return return
end end
if eventName == "VARIABLES_LOADED" then
engine:Init()
local store = engine:EnsureDefaults()
AB_Trace("bridge VARIABLES_LOADED store.enabled=" .. tostring(store and store.enabled)
.. " projectiles.enabled=" .. tostring(store and store.projectiles and store.projectiles.enabled))
return
end
if eventName == "MERCHANT_SHOW" or eventName == "MERCHANT_UPDATE" then if eventName == "MERCHANT_SHOW" or eventName == "MERCHANT_UPDATE" then
AB_Trace("bridge " .. tostring(eventName) .. " -> engine") AB_Trace("bridge " .. tostring(eventName) .. " -> engine")
engine:OnMerchantEvent(eventName) engine:OnMerchantEvent(eventName)
@@ -75,6 +66,34 @@ local function AB_EnsureBridgeFrame(moduleRef)
return frame return frame
end end
local function AB_SetBridgeActive(active)
local frame = MTH_AutoBuy._bridgeFrame
if active then
if not frame then
frame = AB_EnsureBridgeFrame(MTH_AutoBuy)
end
if frame then
frame:RegisterEvent("MERCHANT_SHOW")
frame:RegisterEvent("MERCHANT_UPDATE")
frame:RegisterEvent("MERCHANT_CLOSED")
MTH_AutoBuy._bridgeActive = true
AB_Trace("bridge active=true")
else
MTH_AutoBuy._bridgeActive = false
AB_Trace("bridge active=false (frame missing)")
end
return
end
if frame then
frame:UnregisterEvent("MERCHANT_SHOW")
frame:UnregisterEvent("MERCHANT_UPDATE")
frame:UnregisterEvent("MERCHANT_CLOSED")
end
MTH_AutoBuy._bridgeActive = false
AB_Trace("bridge active=false")
end
AB_Engine = function() AB_Engine = function()
return MTH_AutoBuyEngine return MTH_AutoBuyEngine
end end
@@ -87,8 +106,8 @@ function MTH_AutoBuy:init()
return return
end end
engine:Init() engine:Init()
AB_EnsureBridgeFrame(self)
self.initialized = true self.initialized = true
AB_SetBridgeActive(self.enabled and true or false)
AB_Trace("init done") AB_Trace("init done")
if self.enabled then if self.enabled then
AB_Log("module initialized", "debug") AB_Log("module initialized", "debug")
@@ -106,7 +125,7 @@ function MTH_AutoBuy:setEnabled(enabled)
end end
local store = engine:EnsureDefaults() local store = engine:EnsureDefaults()
store.enabled = enabled and true or false store.enabled = enabled and true or false
AB_EnsureBridgeFrame(self) AB_SetBridgeActive(store.enabled and true or false)
AB_Trace("setEnabled store.enabled=" .. tostring(store.enabled)) AB_Trace("setEnabled store.enabled=" .. tostring(store.enabled))
end end
@@ -125,7 +144,7 @@ function MTH_AutoBuy:onEvent(event)
if event == "VARIABLES_LOADED" then if event == "VARIABLES_LOADED" then
engine:Init() engine:Init()
AB_EnsureBridgeFrame(self) AB_SetBridgeActive(self.enabled and true or false)
AB_Log("onEvent VARIABLES_LOADED -> engine init", "debug") AB_Log("onEvent VARIABLES_LOADED -> engine init", "debug")
local store = engine:EnsureDefaults() local store = engine:EnsureDefaults()
AB_Trace("VARIABLES_LOADED module.enabled=" .. tostring(self.enabled) AB_Trace("VARIABLES_LOADED module.enabled=" .. tostring(self.enabled)
+2 -1
View File
@@ -45,7 +45,8 @@ function MTH_ChronometerHunterInstallData(engine)
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 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 = "YELLOW", xn = BS["Wing Clip"] }) engine:AddTimer(engine.EVENT, BS["Improved Wing Clip"], 5, 1, 0, 0, { a = 1, cr = "YELLOW", xn = BS["Wing Clip"] })
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["Piercing Shot"], 8, 1, 0, 1, { a = 1, cr = "PURPLE", tx = "Interface\\Icons\\Ability_Hunter_DisarmingShot" })
engine:AddTimer(engine.EVENT, BS["Quick Shots"], 12, 0, 1, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\Ability_Hunter_Quickshot" }) engine:AddTimer(engine.EVENT, BS["Quick Shots"], 12, 0, 1, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\Ability_Warrior_InnerRage" })
engine:AddTimer(engine.EVENT, BS["Wing Clip"], 10, 1, 0, 1, { a = 1, cr = "YELLOW" })
engine:AddTimer(engine.EVENT, BS["Scorpid Poison"], 10, 1, 0, 0, { gr = 3, cr = "PURPLE", a = 1, dn = 1 }) engine:AddTimer(engine.EVENT, BS["Scorpid Poison"], 10, 1, 0, 0, { gr = 3, cr = "PURPLE", a = 1, dn = 1 })
engine:AddTimer(engine.EVENT, BS["Devilsaur Fury"], 20, 0, 1, 1, { a = 1, cr = "PURPLE", xn = BS["Devilsaur Eye"], tx = "Interface\\Icons\\Ability_Hunter_Pet_Raptor" }) engine:AddTimer(engine.EVENT, BS["Devilsaur Fury"], 20, 0, 1, 1, { a = 1, cr = "PURPLE", xn = BS["Devilsaur Eye"], tx = "Interface\\Icons\\Ability_Hunter_Pet_Raptor" })
engine:AddTimer(engine.EVENT, BS["Restless Strength"], 20, 0, 1, 1, { a = 1, cr = "PURPLE", xn = BS["Zandalarian Hero Medallion"], tx = "Interface\\Icons\\INV_Jewelry_Necklace_13" }) engine:AddTimer(engine.EVENT, BS["Restless Strength"], 20, 0, 1, 1, { a = 1, cr = "PURPLE", xn = BS["Zandalarian Hero Medallion"], tx = "Interface\\Icons\\INV_Jewelry_Necklace_13" })
+22 -2
View File
@@ -143,12 +143,32 @@ local function shallowCopy(source)
return dest return dest
end end
local function deepCopyValue(value)
if type(value) ~= "table" then
return value
end
local copy = {}
for key, entry in pairs(value) do
copy[key] = deepCopyValue(entry)
end
return copy
end
local function ensureProfile() local function ensureProfile()
if not MTH or not MTH.GetModuleSavedVariables then if not MTH or not MTH.GetModuleCharSavedVariables then
return shallowCopy(DEFAULTS) return shallowCopy(DEFAULTS)
end end
local store = MTH:GetModuleSavedVariables("chronometer") local store = MTH:GetModuleCharSavedVariables("chronometer")
if type(store) ~= "table" then
return shallowCopy(DEFAULTS)
end
if type(store.profile) ~= "table" and MTH.GetModuleSavedVariables then
local accountStore = MTH:GetModuleSavedVariables("chronometer")
if type(accountStore) == "table" and type(accountStore.profile) == "table" then
store.profile = deepCopyValue(accountStore.profile)
end
end
if type(store.profile) ~= "table" then if type(store.profile) ~= "table" then
store.profile = {} store.profile = {}
end end
+1 -1
View File
@@ -1,7 +1,7 @@
local MTH_ChronometerModule = { local MTH_ChronometerModule = {
name = "chronometer", name = "chronometer",
enabled = true, enabled = true,
version = "1.0.4", version = "1.0.6",
events = {}, events = {},
initialized = false, initialized = false,
} }
+35 -48
View File
@@ -6,7 +6,7 @@
local MTH_FeedOMatic = { local MTH_FeedOMatic = {
name = "feedomatic", name = "feedomatic",
enabled = false, enabled = false,
version = "1.0.4", version = "1.0.6",
events = { events = {
"VARIABLES_LOADED", "VARIABLES_LOADED",
"MERCHANT_SHOW", "MERCHANT_SHOW",
@@ -137,47 +137,31 @@ local function MTH_FeedOMatic_SyncSavedVariables()
moduleStore.legacy = {} moduleStore.legacy = {}
end end
if type(FOM_Config) == "table" then local function bindLegacyTable(globalName)
moduleStore.legacy.FOM_Config = FOM_Config local legacyValue = moduleStore.legacy[globalName]
elseif type(moduleStore.legacy.FOM_Config) == "table" then local globalValue = _G and _G[globalName] or nil
FOM_Config = moduleStore.legacy.FOM_Config
if type(legacyValue) ~= "table" then
if type(globalValue) == "table" then
legacyValue = globalValue
else
legacyValue = {}
end
moduleStore.legacy[globalName] = legacyValue
end
if _G then
_G[globalName] = legacyValue
end
end end
if type(FOM_FoodQuality) == "table" then bindLegacyTable("FOM_Config")
moduleStore.legacy.FOM_FoodQuality = FOM_FoodQuality bindLegacyTable("FOM_FoodQuality")
elseif type(moduleStore.legacy.FOM_FoodQuality) == "table" then bindLegacyTable("FOM_AddedFoods")
FOM_FoodQuality = moduleStore.legacy.FOM_FoodQuality bindLegacyTable("FOM_RemovedFoods")
end bindLegacyTable("FOM_Cooking")
bindLegacyTable("FOM_QuestFood")
if type(FOM_AddedFoods) == "table" then bindLegacyTable("FOM_LocaleInfo")
moduleStore.legacy.FOM_AddedFoods = FOM_AddedFoods
elseif type(moduleStore.legacy.FOM_AddedFoods) == "table" then
FOM_AddedFoods = moduleStore.legacy.FOM_AddedFoods
end
if type(FOM_RemovedFoods) == "table" then
moduleStore.legacy.FOM_RemovedFoods = FOM_RemovedFoods
elseif type(moduleStore.legacy.FOM_RemovedFoods) == "table" then
FOM_RemovedFoods = moduleStore.legacy.FOM_RemovedFoods
end
if type(FOM_Cooking) == "table" then
moduleStore.legacy.FOM_Cooking = FOM_Cooking
elseif type(moduleStore.legacy.FOM_Cooking) == "table" then
FOM_Cooking = moduleStore.legacy.FOM_Cooking
end
if type(FOM_QuestFood) == "table" then
moduleStore.legacy.FOM_QuestFood = FOM_QuestFood
elseif type(moduleStore.legacy.FOM_QuestFood) == "table" then
FOM_QuestFood = moduleStore.legacy.FOM_QuestFood
end
if type(FOM_LocaleInfo) == "table" then
moduleStore.legacy.FOM_LocaleInfo = FOM_LocaleInfo
elseif type(moduleStore.legacy.FOM_LocaleInfo) == "table" then
FOM_LocaleInfo = moduleStore.legacy.FOM_LocaleInfo
end
end end
local function MTH_FeedOMatic_Log(message, severity) local function MTH_FeedOMatic_Log(message, severity)
@@ -231,6 +215,14 @@ local function MTH_FeedOMatic_DebugMerchantScan(sourceEvent)
end end
local function MTH_FeedOMatic_SetMerchantProbeEnabled(enabled) local function MTH_FeedOMatic_SetMerchantProbeEnabled(enabled)
if not enabled then
if MTH_FeedOMatic_MerchantProbeFrame then
MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_SHOW")
MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_UPDATE")
end
return
end
if not MTH_FeedOMatic_MerchantProbeFrame then if not MTH_FeedOMatic_MerchantProbeFrame then
MTH_FeedOMatic_MerchantProbeFrame = CreateFrame("Frame", "MTH_FeedOMatic_MerchantProbeFrame") MTH_FeedOMatic_MerchantProbeFrame = CreateFrame("Frame", "MTH_FeedOMatic_MerchantProbeFrame")
MTH_FeedOMatic_MerchantProbeFrame:SetScript("OnEvent", function() MTH_FeedOMatic_MerchantProbeFrame:SetScript("OnEvent", function()
@@ -242,14 +234,9 @@ local function MTH_FeedOMatic_SetMerchantProbeEnabled(enabled)
end) end)
end end
if enabled then MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_SHOW")
MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_SHOW") MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_UPDATE")
MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_UPDATE") MTH_FeedOMatic_Log("merchant probe enabled", "debug")
MTH_FeedOMatic_Log("merchant probe enabled", "debug")
else
MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_SHOW")
MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_UPDATE")
end
end end
function MTH_FeedOMatic:init() function MTH_FeedOMatic:init()
+2 -1
View File
@@ -155,7 +155,8 @@ MTH_SA_List = {}
MTH_SA_LastCheck = 0 MTH_SA_LastCheck = 0
MTH_SA_Tooltip = CreateFrame("GameTooltip", "MTH_SA_Tooltip", nil, "GameTooltipTemplate") MTH_SA_Tooltip = CreateFrame("GameTooltip", "MTH_SA_Scan", nil, "GameTooltipTemplate")
MTH_SA_TooltipTextLeft1 = getglobal("MTH_SA_ScanTextLeft1")
MTH_SA_Tooltip:Hide() MTH_SA_Tooltip:Hide()
local _, class = UnitClass("player") local _, class = UnitClass("player")
+1 -1
View File
@@ -7,7 +7,7 @@ MTH_SA_MANAGED_HOOKS = true
local MTH_SmartAmmo = { local MTH_SmartAmmo = {
name = "smartammo", name = "smartammo",
enabled = true, enabled = true,
version = "1.0.4", version = "1.0.6",
events = { events = {
"VARIABLES_LOADED", "VARIABLES_LOADED",
"PLAYER_ENTERING_WORLD", "PLAYER_ENTERING_WORLD",
+259 -23
View File
@@ -6,7 +6,7 @@
local MTH_Tooltips = { local MTH_Tooltips = {
name = "tooltips", name = "tooltips",
enabled = true, enabled = true,
version = "1.0.4", version = "1.0.6",
events = { events = {
"UPDATE_MOUSEOVER_UNIT", "UPDATE_MOUSEOVER_UNIT",
"UNIT_NAME_UPDATE", "UNIT_NAME_UPDATE",
@@ -34,14 +34,33 @@ local MTH_Tooltips = {
injectFrame = nil, injectFrame = nil,
pollFrame = nil, pollFrame = nil,
pollElapsed = 0, pollElapsed = 0,
hintElapsed = 0,
lastMouseoverKey = "", lastMouseoverKey = "",
} }
local function MTH_TT_CopyValue(value)
if type(value) ~= "table" then
return value
end
local copy = {}
for key, entry in pairs(value) do
copy[key] = MTH_TT_CopyValue(entry)
end
return copy
end
local function MTH_TT_GetOptionsStore() local function MTH_TT_GetOptionsStore()
if MTH and MTH.GetModuleSavedVariables then if MTH and MTH.GetModuleCharSavedVariables then
local store = MTH:GetModuleSavedVariables("tooltips") local store = MTH:GetModuleCharSavedVariables("tooltips")
if type(store) == "table" then if type(store) == "table" then
if MTH.GetModuleSavedVariables and next(store) == nil then
local accountStore = MTH:GetModuleSavedVariables("tooltips")
if type(accountStore) == "table" and next(accountStore) ~= nil then
for key, value in pairs(accountStore) do
store[key] = MTH_TT_CopyValue(value)
end
end
end
if store.beastTooltips == nil then if store.beastTooltips == nil then
store.beastTooltips = true store.beastTooltips = true
end end
@@ -845,7 +864,37 @@ local function MTH_TT_FindAbilityCanonicalName(abilityName)
return nil return nil
end end
local function MTH_TT_ParseRankFromText(value) local function MTH_TT_AbilityHasPositiveRanks(canonicalAbilityName)
if canonicalAbilityName == nil or canonicalAbilityName == "" then
return false
end
if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then
return false
end
local bucket = MTH_DS_PetSpells.byAbility[canonicalAbilityName]
if not (bucket and type(bucket.spells) == "table") then
return false
end
for i = 1, table.getn(bucket.spells) do
local spell = bucket.spells[i]
if spell then
local rank = tonumber(spell.rankNumber)
if not rank and spell.rank then
local _, _, parsed = string.find(tostring(spell.rank), "(%d+)")
rank = tonumber(parsed)
end
if rank and rank > 0 then
return true
end
end
end
return false
end
local function MTH_TT_ParseRankFromText(value, allowTrailingNumber)
local text = MTH_TT_Trim(value) local text = MTH_TT_Trim(value)
if text == "" then if text == "" then
return nil return nil
@@ -854,9 +903,11 @@ local function MTH_TT_ParseRankFromText(value)
if rankText then if rankText then
return tonumber(rankText) return tonumber(rankText)
end end
local _, _, trailing = string.find(text, "(%d+)$") if allowTrailingNumber == true then
if trailing then local _, _, trailing = string.find(text, "(%d+)$")
return tonumber(trailing) if trailing then
return tonumber(trailing)
end
end end
return nil return nil
end end
@@ -884,10 +935,10 @@ local function MTH_TT_GetTooltipSpellNameAndRank()
if ok then if ok then
if type(a) == "string" then if type(a) == "string" then
spellName = a spellName = a
rankNumber = MTH_TT_ParseRankFromText(b) rankNumber = MTH_TT_ParseRankFromText(b, true)
elseif type(b) == "string" then elseif type(b) == "string" then
spellName = b spellName = b
rankNumber = MTH_TT_ParseRankFromText(a) rankNumber = MTH_TT_ParseRankFromText(a, true)
end end
end end
end end
@@ -899,24 +950,28 @@ local function MTH_TT_GetTooltipSpellNameAndRank()
end end
end end
if not rankNumber then
rankNumber = MTH_TT_ParseRankFromText(spellName, true)
end
if not rankNumber then if not rankNumber then
local right1 = getglobal("GameTooltipTextRight1") local right1 = getglobal("GameTooltipTextRight1")
if right1 and right1.GetText then if right1 and right1.GetText then
rankNumber = MTH_TT_ParseRankFromText(right1:GetText()) rankNumber = MTH_TT_ParseRankFromText(right1:GetText(), false)
end end
end end
if not rankNumber then if not rankNumber then
local left2 = getglobal("GameTooltipTextLeft2") local left2 = getglobal("GameTooltipTextLeft2")
if left2 and left2.GetText then if left2 and left2.GetText then
rankNumber = MTH_TT_ParseRankFromText(left2:GetText()) rankNumber = MTH_TT_ParseRankFromText(left2:GetText(), false)
end end
end end
if not rankNumber then if not rankNumber then
local right2 = getglobal("GameTooltipTextRight2") local right2 = getglobal("GameTooltipTextRight2")
if right2 and right2.GetText then if right2 and right2.GetText then
rankNumber = MTH_TT_ParseRankFromText(right2:GetText()) rankNumber = MTH_TT_ParseRankFromText(right2:GetText(), false)
end end
end end
@@ -925,7 +980,7 @@ local function MTH_TT_GetTooltipSpellNameAndRank()
local left = getglobal("GameTooltipTextLeft" .. i) local left = getglobal("GameTooltipTextLeft" .. i)
if left and left.GetText then if left and left.GetText then
local text = MTH_TT_Trim(left:GetText()) local text = MTH_TT_Trim(left:GetText())
local parsed = MTH_TT_ParseRankFromText(text) local parsed = MTH_TT_ParseRankFromText(text, false)
if parsed and parsed > 0 then if parsed and parsed > 0 then
rankNumber = parsed rankNumber = parsed
break break
@@ -933,7 +988,7 @@ local function MTH_TT_GetTooltipSpellNameAndRank()
end end
local right = getglobal("GameTooltipTextRight" .. i) local right = getglobal("GameTooltipTextRight" .. i)
if right and right.GetText then if right and right.GetText then
local parsedRight = MTH_TT_ParseRankFromText(right:GetText()) local parsedRight = MTH_TT_ParseRankFromText(right:GetText(), false)
if parsedRight and parsedRight > 0 then if parsedRight and parsedRight > 0 then
rankNumber = parsedRight rankNumber = parsedRight
break break
@@ -945,6 +1000,87 @@ local function MTH_TT_GetTooltipSpellNameAndRank()
return MTH_TT_CleanSpellName(spellName), rankNumber return MTH_TT_CleanSpellName(spellName), rankNumber
end end
local function MTH_TT_GetCurrentPetRowForTooltip()
local pets = nil
if type(MTH_PETS_GetRootStore) == "function" then
pets = MTH_PETS_GetRootStore()
elseif type(MTH_CharSavedVariables) == "table" then
pets = MTH_CharSavedVariables.MTH_Pets
end
if type(pets) ~= "table" then
return nil
end
local currentId = pets.currentPetId
if (currentId == nil or tostring(currentId) == "") and type(pets.currentPet) == "table" and pets.currentPet.exists == true then
currentId = pets.currentPet.id
end
if currentId == nil or tostring(currentId) == "" then
return nil
end
local activeById = type(pets.petStore) == "table" and pets.petStore.activeById or nil
if type(activeById) ~= "table" then
return nil
end
return activeById[currentId] or activeById[tostring(currentId)]
end
local function MTH_TT_CurrentPetKnowsAbility(abilityLower, rankNumber)
if abilityLower == "" then
return false
end
local row = MTH_TT_GetCurrentPetRowForTooltip()
if type(row) ~= "table" then
return false
end
local wantedRank = tonumber(rankNumber)
local spellbook = row.petSpellbook
if type(spellbook) == "table" and type(spellbook.spells) == "table" then
for i = 1, table.getn(spellbook.spells) do
local spell = spellbook.spells[i]
if type(spell) == "table" then
local spellLower = MTH_TT_Lower(MTH_TT_CleanSpellName(spell.name))
if spellLower == abilityLower then
if wantedRank then
local spellRank = tonumber(spell.rank)
if not spellRank then
spellRank = MTH_TT_ParseRankFromText(spell.name, true)
end
if spellRank and tonumber(spellRank) == wantedRank then
return true
end
else
return true
end
end
end
end
end
if type(row.abilities) == "table" then
for _, ability in pairs(row.abilities) do
if type(ability) == "table" then
local abilityLowerName = MTH_TT_Lower(MTH_TT_CleanSpellName(ability.name))
if abilityLowerName == abilityLower then
if wantedRank then
local abilityRank = tonumber(ability.rank)
if abilityRank and abilityRank == wantedRank then
return true
end
else
return true
end
end
end
end
end
return false
end
local function MTH_TT_IsPetActionTooltipOwner() local function MTH_TT_IsPetActionTooltipOwner()
if not GameTooltip or type(GameTooltip.GetOwner) ~= "function" then if not GameTooltip or type(GameTooltip.GetOwner) ~= "function" then
return false return false
@@ -1031,7 +1167,15 @@ local function MTH_TT_AddPetActionNotLearnedHint()
return return
end end
local known = MTH_TT_HunterKnowsAbility(MTH_TT_Lower(canonical), rankNumber) if rankNumber and rankNumber > 0 and not MTH_TT_AbilityHasPositiveRanks(canonical) then
rankNumber = nil
end
local canonicalLower = MTH_TT_Lower(canonical)
local known = MTH_TT_HunterKnowsAbility(canonicalLower, rankNumber)
if not known then
known = MTH_TT_CurrentPetKnowsAbility(canonicalLower, rankNumber)
end
if known then if known then
return return
end end
@@ -1165,16 +1309,21 @@ local function MTH_TT_EnsureInjectFrame()
end end
local frame = CreateFrame("Frame", "MTH_TooltipsInjectFrame") local frame = CreateFrame("Frame", "MTH_TooltipsInjectFrame")
frame.pending = false frame.pending = false
frame:SetScript("OnUpdate", function(self) frame.onUpdate = function(self)
self = self or this self = self or this
if not self or not self.pending then if not self then
return
end
if not self.pending then
self:SetScript("OnUpdate", nil)
return return
end end
self.pending = false self.pending = false
self:SetScript("OnUpdate", nil)
if MTH_Tooltips and MTH_Tooltips.enabled then if MTH_Tooltips and MTH_Tooltips.enabled then
MTH_TT_AddTooltip("mouseover") MTH_TT_AddTooltip("mouseover")
end end
end) end
MTH_Tooltips.injectFrame = frame MTH_Tooltips.injectFrame = frame
return frame return frame
end end
@@ -1183,6 +1332,9 @@ local function MTH_TT_RequestInject()
local frame = MTH_TT_EnsureInjectFrame() local frame = MTH_TT_EnsureInjectFrame()
if frame then if frame then
frame.pending = true frame.pending = true
if not frame:GetScript("OnUpdate") then
frame:SetScript("OnUpdate", frame.onUpdate)
end
end end
end end
@@ -1197,14 +1349,18 @@ local function MTH_TT_EnsurePollFrame()
return return
end end
if GameTooltip and GameTooltip.IsShown and GameTooltip:IsShown() then elapsed = elapsed or arg1
MTH_TT_AddOwnPetTooltipHint() MTH_Tooltips.hintElapsed = (MTH_Tooltips.hintElapsed or 0) + (elapsed or 0)
if not MTH_TT_HasExistingNotLearnedLine() then if MTH_Tooltips.hintElapsed >= 0.25 then
MTH_TT_AddPetActionNotLearnedHint() MTH_Tooltips.hintElapsed = 0
if GameTooltip and GameTooltip.IsShown and GameTooltip:IsShown() then
MTH_TT_AddOwnPetTooltipHint()
if not MTH_TT_HasExistingNotLearnedLine() then
MTH_TT_AddPetActionNotLearnedHint()
end
end end
end end
elapsed = elapsed or arg1
MTH_Tooltips.pollElapsed = (MTH_Tooltips.pollElapsed or 0) + (elapsed or 0) MTH_Tooltips.pollElapsed = (MTH_Tooltips.pollElapsed or 0) + (elapsed or 0)
if MTH_Tooltips.pollElapsed < 0.15 then if MTH_Tooltips.pollElapsed < 0.15 then
return return
@@ -1242,6 +1398,75 @@ local function MTH_TT_EnsurePollFrame()
return frame return frame
end end
local function MTH_TT_SetRuntimeActive(active)
local injectFrame = MTH_Tooltips.injectFrame
if injectFrame then
if not active then
injectFrame.pending = false
injectFrame:SetScript("OnUpdate", nil)
end
end
local pollFrame = MTH_Tooltips.pollFrame
if pollFrame then
if active then
pollFrame:SetScript("OnUpdate", pollFrame:GetScript("OnUpdate") or function(_, elapsed)
if not (MTH_Tooltips and MTH_Tooltips.enabled) then
return
end
elapsed = elapsed or arg1
MTH_Tooltips.hintElapsed = (MTH_Tooltips.hintElapsed or 0) + (elapsed or 0)
if MTH_Tooltips.hintElapsed >= 0.25 then
MTH_Tooltips.hintElapsed = 0
if GameTooltip and GameTooltip.IsShown and GameTooltip:IsShown() then
MTH_TT_AddOwnPetTooltipHint()
if not MTH_TT_HasExistingNotLearnedLine() then
MTH_TT_AddPetActionNotLearnedHint()
end
end
end
MTH_Tooltips.pollElapsed = (MTH_Tooltips.pollElapsed or 0) + (elapsed or 0)
if MTH_Tooltips.pollElapsed < 0.15 then
return
end
MTH_Tooltips.pollElapsed = 0
local state = MTH_Tooltips.debugState
state.pollTicks = (state.pollTicks or 0) + 1
if not UnitExists("mouseover") then
MTH_Tooltips.lastMouseoverKey = ""
return
end
local name = UnitName("mouseover")
if not name or name == "" then
return
end
local lvl = UnitLevel and UnitLevel("mouseover") or 0
local key = tostring(name) .. "#" .. tostring(lvl)
if key ~= MTH_Tooltips.lastMouseoverKey then
MTH_Tooltips.lastMouseoverKey = key
state.pollTriggers = (state.pollTriggers or 0) + 1
MTH_TT_RequestInject()
return
end
if not MTH_TT_HasExistingTooltipLine() then
MTH_TT_RequestInject()
end
end)
else
MTH_Tooltips.pollElapsed = 0
MTH_Tooltips.hintElapsed = 0
pollFrame:SetScript("OnUpdate", nil)
end
end
end
local function MTH_TT_InstallTooltipHook() local function MTH_TT_InstallTooltipHook()
if MTH_Tooltips.hookInstalled then if MTH_Tooltips.hookInstalled then
return return
@@ -1260,9 +1485,15 @@ local function MTH_TT_InstallTooltipHook()
MTH_TT_RequestInject() MTH_TT_RequestInject()
end) end)
GameTooltip:HookScript("OnTooltipSetItem", function() GameTooltip:HookScript("OnTooltipSetItem", function()
if not (MTH_Tooltips and MTH_Tooltips.enabled) then
return
end
MTH_TT_AddFoodTooltipHint() MTH_TT_AddFoodTooltipHint()
end) end)
GameTooltip:HookScript("OnTooltipSetSpell", function() GameTooltip:HookScript("OnTooltipSetSpell", function()
if not (MTH_Tooltips and MTH_Tooltips.enabled) then
return
end
MTH_TT_AddPetActionNotLearnedHint() MTH_TT_AddPetActionNotLearnedHint()
end) end)
end) end)
@@ -1284,6 +1515,7 @@ function MTH_Tooltips:init()
MTH_TT_InstallTooltipHook() MTH_TT_InstallTooltipHook()
MTH_TT_EnsureInjectFrame() MTH_TT_EnsureInjectFrame()
MTH_TT_EnsurePollFrame() MTH_TT_EnsurePollFrame()
MTH_TT_SetRuntimeActive(self.enabled)
MTH_TT_Log("module init complete") MTH_TT_Log("module init complete")
end end
@@ -1307,6 +1539,9 @@ function MTH_Tooltips:setEnabled(enabled)
elseif self.enabled then elseif self.enabled then
MTH_TT_EnsurePollFrame() MTH_TT_EnsurePollFrame()
MTH_TT_EnsureInjectFrame() MTH_TT_EnsureInjectFrame()
MTH_TT_SetRuntimeActive(true)
else
MTH_TT_SetRuntimeActive(false)
end end
end end
@@ -1357,6 +1592,7 @@ end
function MTH_Tooltips:cleanup() function MTH_Tooltips:cleanup()
self.enabled = false self.enabled = false
MTH_TT_SetRuntimeActive(false)
end end
function MTH_Tooltips:GetDebugSnapshot() function MTH_Tooltips:GetDebugSnapshot()
+2 -1
View File
@@ -11,7 +11,8 @@ BINDING_HEADER_ZMountsHeader = "ZMounts Buttons"
BINDING_HEADER_ZCompanionsHeader = "ZCompanions Buttons" BINDING_HEADER_ZCompanionsHeader = "ZCompanions Buttons"
BINDING_HEADER_ZToysHeader = "ZToys Buttons" BINDING_HEADER_ZToysHeader = "ZToys Buttons"
ZHunterModTooltip = CreateFrame("GameTooltip", "ZHunterModTooltip", nil, "GameTooltipTemplate") ZHunterModTooltip = CreateFrame("GameTooltip", "ZHunterModScan", nil, "GameTooltipTemplate")
ZHunterModTooltipTextLeft1 = getglobal("ZHunterModScanTextLeft1")
function MTH_ZH_IsModuleEnabled() function MTH_ZH_IsModuleEnabled()
if not (MTH and MTH.IsModuleEnabled) then if not (MTH and MTH.IsModuleEnabled) then
+58 -4
View File
@@ -384,9 +384,36 @@ function ZSpellButton_OnEnter()
if this.isspell then if this.isspell then
GameTooltip:SetSpell(this.id, "spell") GameTooltip:SetSpell(this.id, "spell")
else else
GameTooltip:SetText(this.ammocount .. " x "..this.ammolink, 1, 1, 1) local showedItemTooltip = nil
--GameTooltip:AddLine("Q="..this.ammocount) if this.ammobag and this.ammoslot and type(GameTooltip.SetBagItem) == "function" then
GameTooltip:ClearLines()
GameTooltip:SetBagItem(this.ammobag, this.ammoslot)
if type(GameTooltip.NumLines) == "function" and GameTooltip:NumLines() > 0 then
local left1 = getglobal("GameTooltipTextLeft1")
if left1 and left1.GetText and tostring(left1:GetText() or "") ~= "" then
showedItemTooltip = true
end
end
end
if (not showedItemTooltip) and this.ammolink and type(GameTooltip.SetHyperlink) == "function" then
GameTooltip:ClearLines()
GameTooltip:SetHyperlink(this.ammolink)
if type(GameTooltip.NumLines) == "function" and GameTooltip:NumLines() > 0 then
local left1 = getglobal("GameTooltipTextLeft1")
if left1 and left1.GetText and tostring(left1:GetText() or "") ~= "" then
showedItemTooltip = true
end
end
end
if not showedItemTooltip then
local label = this.ammoname or this.ammolink or "Ammo"
if this.ammocount then
label = tostring(this.ammocount) .. " x " .. tostring(label)
end
GameTooltip:SetText(label, 1, 1, 1)
end
end end
GameTooltip:Show()
end end
end end
@@ -488,11 +515,38 @@ function ZSpellButtonParent_OnEnter(frame)
if string.len(rank) > 0 then if string.len(rank) > 0 then
msg = msg.." ("..rank..")" msg = msg.." ("..rank..")"
end end
GameTooltip:SetText(msg, 1, 1, 1)
else else
msg = frame.ammoname local showedItemTooltip = nil
if frame.ammobag and frame.ammoslot and type(GameTooltip.SetBagItem) == "function" then
GameTooltip:ClearLines()
GameTooltip:SetBagItem(frame.ammobag, frame.ammoslot)
if type(GameTooltip.NumLines) == "function" and GameTooltip:NumLines() > 0 then
local left1 = getglobal("GameTooltipTextLeft1")
if left1 and left1.GetText and tostring(left1:GetText() or "") ~= "" then
showedItemTooltip = true
end
end
end
if (not showedItemTooltip) and frame.ammolink and type(GameTooltip.SetHyperlink) == "function" then
GameTooltip:ClearLines()
GameTooltip:SetHyperlink(frame.ammolink)
if type(GameTooltip.NumLines) == "function" and GameTooltip:NumLines() > 0 then
local left1 = getglobal("GameTooltipTextLeft1")
if left1 and left1.GetText and tostring(left1:GetText() or "") ~= "" then
showedItemTooltip = true
end
end
end
if not showedItemTooltip then
msg = frame.ammoname or frame.ammolink or "Ammo"
if frame.ammocount then
msg = tostring(frame.ammocount) .. " x " .. tostring(msg)
end
GameTooltip:SetText(msg, 1, 1, 1)
end
end end
GameTooltip:SetText(msg, 1, 1, 1)
GameTooltip:AddLine("Alt+Drag To Move This Button") GameTooltip:AddLine("Alt+Drag To Move This Button")
GameTooltip:Show() GameTooltip:Show()
end end
+54 -1
View File
@@ -8,7 +8,7 @@ MTH_ZH_MANAGED_HOOKS = true
local MTH_ZHunter = { local MTH_ZHunter = {
name = "zhunter", name = "zhunter",
enabled = true, enabled = true,
version = "1.0.4", version = "1.0.6",
events = { events = {
"VARIABLES_LOADED", "VARIABLES_LOADED",
"PLAYER_ENTERING_WORLD", "PLAYER_ENTERING_WORLD",
@@ -288,6 +288,57 @@ MTH_ZH_SetAuxFramesVisible = function(visible)
return return
end end
local function MTH_ZH_SetAdjustmentHandlersActive(active)
local frameSpecs = {
{ frame = "zButtonAspectAdjustment", handler = "zButtonAspectAdjustment_OnEvent", getter = "zButtonAspect_GetSaved" },
{ frame = "zButtonTrackAdjustment", handler = "zButtonTrackAdjustment_OnEvent", getter = "zButtonTrack_GetSaved" },
{ frame = "zButtonTrapAdjustment", handler = "zButtonTrapAdjustment_OnEvent", getter = "zButtonTrap_GetSaved" },
{ frame = "zButtonRangedAdjustment", handler = "zButtonRangedAdjustment_OnEvent", getter = "zButtonRanged_GetSaved" },
{ frame = "zButtonAmmoAdjustment", handler = "zButtonAmmoAdjustment_OnEvent", getter = "zButtonAmmo_GetSaved" },
{ frame = "zButtonPetAdjustment", handler = "zButtonPetAdjustment_OnEvent", getter = "zButtonPet_GetSaved" },
{ frame = "zButtonMountsAdjustment", handler = "zButtonMountsAdjustment_OnEvent", getter = "zButtonMounts_GetSaved" },
{ frame = "zButtonCompanionsAdjustment", handler = "zButtonCompanionsAdjustment_OnEvent", getter = "zButtonCompanions_GetSaved" },
{ frame = "zButtonToysAdjustment", handler = "zButtonToysAdjustment_OnEvent", getter = "zButtonToys_GetSaved" },
}
local function isFeatureEnabled(getterName)
local getter = getglobal(getterName)
if type(getter) ~= "function" then
return true
end
local ok, saved = pcall(getter)
if not ok or type(saved) ~= "table" then
return true
end
if saved["enabled"] == false or saved["enabled"] == 0 then
return false
end
return true
end
for _, spec in ipairs(frameSpecs) do
local frameName = spec.frame
local handlerName = spec.handler
local frame = getglobal(frameName)
if frame and frame.SetScript then
if active then
if isFeatureEnabled(spec.getter) then
local handler = getglobal(handlerName)
if type(handler) == "function" then
frame:SetScript("OnEvent", handler)
else
frame:SetScript("OnEvent", nil)
end
else
frame:SetScript("OnEvent", nil)
end
else
frame:SetScript("OnEvent", nil)
end
end
end
end
local function MTH_ZH_ClearRuntimeFeatureFlags() local function MTH_ZH_ClearRuntimeFeatureFlags()
return return
end end
@@ -310,9 +361,11 @@ function MTH_ZHunter:setEnabled(enabled)
MTH_ZH_SyncSavedVariables() MTH_ZH_SyncSavedVariables()
if enabled then if enabled then
MTH_ZH_EnsureBootstrapRestoreFrame() MTH_ZH_EnsureBootstrapRestoreFrame()
MTH_ZH_SetAdjustmentHandlersActive(true)
MTH_ZH_ApplyEnabledRuntimeState("setEnabled") MTH_ZH_ApplyEnabledRuntimeState("setEnabled")
else else
MTH_ZH_ClearRuntimeFeatureFlags() MTH_ZH_ClearRuntimeFeatureFlags()
MTH_ZH_SetAdjustmentHandlersActive(false)
MTH_ZH_SetButtonsVisible(false) MTH_ZH_SetButtonsVisible(false)
MTH_ZH_SetAuxFramesVisible(false) MTH_ZH_SetAuxFramesVisible(false)
end end
+238 -45
View File
@@ -29,6 +29,69 @@ ZHunterMod_Ammo_Buttons = nil
local ZHUNTER_AMMO_MAX_COUNT = math.max(table.getn(MTH_AMMO_ARROWS or {}), table.getn(MTH_AMMO_BULLETS or {})) local ZHUNTER_AMMO_MAX_COUNT = math.max(table.getn(MTH_AMMO_ARROWS or {}), table.getn(MTH_AMMO_BULLETS or {}))
local zButtonAmmo_Cache = {} local zButtonAmmo_Cache = {}
local zButtonAmmo_LastCacheSignature = nil local zButtonAmmo_LastCacheSignature = nil
local zButtonAmmo_LastRefreshAt = 0
local zButtonAmmo_MinRefreshInterval = 0.20
local zButtonAmmo_MinBagRefreshInterval = 0.75
local zButtonAmmo_KnownAmmoTypes = nil
local zButtonAmmo_AmmoBags = {}
local zButtonAmmo_LastFullScanAt = 0
local zButtonAmmo_MinUnknownBagScanInterval = 5.0
local zButtonAmmo_EmptyScanStreak = 0
local zButtonAmmo_GetKnownAmmoTypes
local function zButtonAmmo_BagHasAmmoNow(bagId)
local bag = tonumber(bagId)
if not bag or bag < 0 or bag > 4 then
return false
end
local knownAmmo = zButtonAmmo_GetKnownAmmoTypes and zButtonAmmo_GetKnownAmmoTypes() or nil
for slot = 1, (GetContainerNumSlots(bag) or 0) do
local link = GetContainerItemLink(bag, slot)
if link then
local _, _, linkName = string.find(link, "%[(.+)%]")
if linkName and knownAmmo and knownAmmo[linkName] then
return true
end
local _, _, id = string.find(link, "Hitem:((%d+).-)")
if id then
local itemName, _, _, _, _, itemSubtype, _, itemSlot = GetItemInfo(id)
if itemSlot == "INVTYPE_AMMO" then
return true
end
if itemName and knownAmmo and knownAmmo[itemName] then
return true
end
if itemSubtype == "Arrow" or itemSubtype == "Bullet" then
return true
end
end
end
end
return false
end
local function zButtonAmmo_UpdateParentCountFast()
if zButtonAmmo and not zButtonAmmo.isspell and zButtonAmmo.ammoname then
local ammoSlot = 0
if GetInventorySlotInfo then
local slotId = GetInventorySlotInfo("AmmoSlot")
if slotId and slotId > 0 then
ammoSlot = slotId
end
end
local slotForCount = ammoSlot and ammoSlot ~= 0 and ammoSlot or 0
if GetInventoryItemCount then
local liveCount = GetInventoryItemCount("player", slotForCount)
local cachedInfo = zButtonAmmo_Cache[zButtonAmmo.ammoname]
if liveCount == 0 and cachedInfo and (tonumber(cachedInfo.count) or 0) > 0 then
zButtonAmmo.ammocount = cachedInfo.count
elseif liveCount ~= nil then
zButtonAmmo.ammocount = liveCount
end
end
zButtonAmmo_UpdateButton(zButtonAmmo)
end
end
local function zButtonAmmo_GetSaved() local function zButtonAmmo_GetSaved()
local currentRoot = zButtonAmmo_GetRoot() local currentRoot = zButtonAmmo_GetRoot()
@@ -38,6 +101,21 @@ local function zButtonAmmo_GetSaved()
return currentRoot["zButtonAmmo"] return currentRoot["zButtonAmmo"]
end end
zButtonAmmo_GetKnownAmmoTypes = function()
if zButtonAmmo_KnownAmmoTypes then
return zButtonAmmo_KnownAmmoTypes
end
local knownAmmo = {}
for i = 1, table.getn(MTH_AMMO_ARROWS or {}) do
knownAmmo[(MTH_AMMO_ARROWS or {})[i]] = "Arrow"
end
for i = 1, table.getn(MTH_AMMO_BULLETS or {}) do
knownAmmo[(MTH_AMMO_BULLETS or {})[i]] = "Bullet"
end
zButtonAmmo_KnownAmmoTypes = knownAmmo
return knownAmmo
end
local zButtonAmmo_ShortArrowLabels = { local zButtonAmmo_ShortArrowLabels = {
[ARROWS_ROUGH] = "Rough", [ARROWS_ROUGH] = "Rough",
[ARROWS_SHARP] = "Sharp", [ARROWS_SHARP] = "Sharp",
@@ -164,13 +242,7 @@ local function zButtonAmmo_GetEquippedAmmoLink()
end end
local function zButtonAmmo_RebuildCache() local function zButtonAmmo_RebuildCache()
local knownAmmo = {} local knownAmmo = zButtonAmmo_GetKnownAmmoTypes()
for i = 1, table.getn(MTH_AMMO_ARROWS or {}) do
knownAmmo[(MTH_AMMO_ARROWS or {})[i]] = "Arrow"
end
for i = 1, table.getn(MTH_AMMO_BULLETS or {}) do
knownAmmo[(MTH_AMMO_BULLETS or {})[i]] = "Bullet"
end
local equippedAmmo = nil local equippedAmmo = nil
local equippedLink = zButtonAmmo_GetEquippedAmmoLink() local equippedLink = zButtonAmmo_GetEquippedAmmoLink()
@@ -185,11 +257,14 @@ local function zButtonAmmo_RebuildCache()
end end
end end
zButtonAmmo_Cache = {} local newCache = {}
local newAmmoBags = {}
local scannedLinkCount = 0
for bag = 0, 4 do for bag = 0, 4 do
for slot = 1, GetContainerNumSlots(bag) do for slot = 1, GetContainerNumSlots(bag) do
local link = GetContainerItemLink(bag, slot) local link = GetContainerItemLink(bag, slot)
if link then if link then
scannedLinkCount = scannedLinkCount + 1
local _, _, id = string.find(link, "Hitem:((%d+).-)") local _, _, id = string.find(link, "Hitem:((%d+).-)")
if id then if id then
local _, _, linkName = string.find(link, "%[(.+)%]") local _, _, linkName = string.find(link, "%[(.+)%]")
@@ -208,7 +283,8 @@ local function zButtonAmmo_RebuildCache()
end end
if isAmmo and ammoName then if isAmmo and ammoName then
local ammoInfo = zButtonAmmo_Cache[ammoName] newAmmoBags[bag] = true
local ammoInfo = newCache[ammoName]
if not ammoInfo then if not ammoInfo then
ammoInfo = { ammoInfo = {
ammo = ammoName, ammo = ammoName,
@@ -224,7 +300,7 @@ local function zButtonAmmo_RebuildCache()
count = 0, count = 0,
equip = nil, equip = nil,
} }
zButtonAmmo_Cache[ammoName] = ammoInfo newCache[ammoName] = ammoInfo
end end
ammoInfo.count = (ammoInfo.count or 0) + (itemCount or 0) ammoInfo.count = (ammoInfo.count or 0) + (itemCount or 0)
if equippedAmmo and equippedAmmo == ammoName then if equippedAmmo and equippedAmmo == ammoName then
@@ -235,6 +311,26 @@ local function zButtonAmmo_RebuildCache()
end end
end end
end end
local hasAmmo = next(newCache) ~= nil
local previousHadAmmo = next(zButtonAmmo_Cache) ~= nil
if hasAmmo then
zButtonAmmo_EmptyScanStreak = 0
zButtonAmmo_Cache = newCache
zButtonAmmo_AmmoBags = newAmmoBags
else
local reliableEmptyScan = scannedLinkCount > 0 and not equippedAmmo
if reliableEmptyScan then
zButtonAmmo_EmptyScanStreak = (zButtonAmmo_EmptyScanStreak or 0) + 1
else
zButtonAmmo_EmptyScanStreak = 0
end
if not previousHadAmmo or zButtonAmmo_EmptyScanStreak >= 3 then
zButtonAmmo_Cache = newCache
zButtonAmmo_AmmoBags = newAmmoBags
end
end
zButtonAmmo_LastFullScanAt = GetTime and GetTime() or zButtonAmmo_LastFullScanAt
end end
local function zButtonAmmo_AssignButtonFromAmmoInfo(button, ammoInfo) local function zButtonAmmo_AssignButtonFromAmmoInfo(button, ammoInfo)
@@ -257,15 +353,41 @@ local function zButtonAmmo_AssignButtonFromAmmoInfo(button, ammoInfo)
return true return true
end end
local function zButtonAmmo_GetCacheSignature() local function zButtonAmmo_GetCacheSignature(ammoList)
local names = {} local hash = 17
for ammoName, ammoInfo in pairs(zButtonAmmo_Cache or {}) do local list = ammoList or {}
if ammoName and ammoInfo and (tonumber(ammoInfo.count) or 0) > 0 then local mod = math.fmod or math.mod
table.insert(names, tostring(ammoName)) for i = 1, table.getn(list) do
local ammoName = list[i]
local ammoInfo = ammoName and zButtonAmmo_Cache[ammoName] or nil
local ammoCount = (ammoInfo and tonumber(ammoInfo.count)) or 0
local value = (hash * 131) + ammoCount + (string.len(tostring(ammoName or "")) * 17)
if mod then
hash = mod(value, 2147483647)
else
hash = value - (math.floor(value / 2147483647) * 2147483647)
end end
end end
table.sort(names) return hash
return table.concat(names, "|") end
local function zButtonAmmo_AssignParentFromChild(child)
if not (zButtonAmmo and child) then
return
end
zButtonAmmo.id = child.ammoid or child.id
zButtonAmmo.icon = child.icon
zButtonAmmo.isspell = nil
zButtonAmmo.ammoname = child.ammoname
zButtonAmmo.ammobrol = child.ammobrol
zButtonAmmo.ammoqual = child.ammoqual
zButtonAmmo.ammolvl = child.ammolvl
zButtonAmmo.ammotype = child.ammotype
zButtonAmmo.ammobag = child.ammobag
zButtonAmmo.ammoslot = child.ammoslot
zButtonAmmo.ammoid = child.ammoid or child.id
zButtonAmmo.ammolink = child.ammolink
zButtonAmmo.ammocount = child.ammocount
end end
local function zButtonAmmo_GetEquippedAmmoInfo() local function zButtonAmmo_GetEquippedAmmoInfo()
@@ -461,7 +583,7 @@ function zButtonAmmo_SetButtons(parent, ammoList)
else else
parent:Show() parent:Show()
end end
zButtonAmmo_LastCacheSignature = zButtonAmmo_GetCacheSignature() zButtonAmmo_LastCacheSignature = zButtonAmmo_GetCacheSignature(ammoList)
return count - 1 return count - 1
end end
@@ -685,6 +807,8 @@ function zButtonAmmo_OnEvent()
zButtonAmmoAdjustment = CreateFrame("Frame", "zButtonAmmoAdjustment") zButtonAmmoAdjustment = CreateFrame("Frame", "zButtonAmmoAdjustment")
zButtonAmmoAdjustment:RegisterEvent("UNIT_INVENTORY_CHANGED") zButtonAmmoAdjustment:RegisterEvent("UNIT_INVENTORY_CHANGED")
zButtonAmmoAdjustment:RegisterEvent("BAG_UPDATE") zButtonAmmoAdjustment:RegisterEvent("BAG_UPDATE")
zButtonAmmoAdjustment:RegisterEvent("PLAYER_ALIVE")
zButtonAmmoAdjustment:RegisterEvent("PLAYER_UNGHOST")
zButtonAmmoAdjustment:SetScript("OnEvent", zButtonAmmoAdjustment_OnEvent) zButtonAmmoAdjustment:SetScript("OnEvent", zButtonAmmoAdjustment_OnEvent)
zButtonAmmo_SetupSizeAndPosition() zButtonAmmo_SetupSizeAndPosition()
zButtonAmmo_DeferredStartupSync() zButtonAmmo_DeferredStartupSync()
@@ -750,11 +874,17 @@ function zButtonAmmo_SetupSizeAndPosition()
zButtonAmmo_EnsureSpellOrder(ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList()) zButtonAmmo_EnsureSpellOrder(ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList())
local saved = zButtonAmmo_GetSaved() local saved = zButtonAmmo_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonAmmoAdjustment and zButtonAmmoAdjustment.SetScript then
zButtonAmmoAdjustment:SetScript("OnEvent", nil)
end
if zButtonAmmo and zButtonAmmo.Hide then if zButtonAmmo and zButtonAmmo.Hide then
zButtonAmmo:Hide() zButtonAmmo:Hide()
end end
return return
end end
if zButtonAmmoAdjustment and zButtonAmmoAdjustment.SetScript then
zButtonAmmoAdjustment:SetScript("OnEvent", zButtonAmmoAdjustment_OnEvent)
end
local ammoList = ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList() local ammoList = ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList()
local arrangeCount = zButtonAmmo.found or table.getn(ammoList) local arrangeCount = zButtonAmmo.found or table.getn(ammoList)
if arrangeCount < 0 then if arrangeCount < 0 then
@@ -801,14 +931,71 @@ function zButtonAmmoAdjustment_OnEvent()
if not zButtonAmmo or not zButtonAmmo.count then if not zButtonAmmo or not zButtonAmmo.count then
return return
end end
if event == "UNIT_INVENTORY_CHANGED" and arg1 and arg1 ~= "player" then
return
end
local saved = zButtonAmmo_GetSaved() local saved = zButtonAmmo_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonAmmoAdjustment and zButtonAmmoAdjustment.SetScript then
zButtonAmmoAdjustment:SetScript("OnEvent", nil)
end
if zButtonAmmo and zButtonAmmo.Hide then
zButtonAmmo:Hide()
end
return
end
if event == "UNIT_INVENTORY_CHANGED" or event == "BAG_UPDATE" then if event == "UNIT_INVENTORY_CHANGED" or event == "BAG_UPDATE" or event == "PLAYER_ALIVE" or event == "PLAYER_UNGHOST" then
local now = GetTime and GetTime() or 0
local bagIsAmmoRelated = false
if event == "BAG_UPDATE" and arg1 ~= nil then
local bagId = tonumber(arg1)
local bagHasAmmoNow = nil
if type(zButtonAmmo_BagHasAmmoNow) == "function" then
bagHasAmmoNow = zButtonAmmo_BagHasAmmoNow
elseif type(_G) == "table" and type(_G["zButtonAmmo_BagHasAmmoNow"]) == "function" then
bagHasAmmoNow = _G["zButtonAmmo_BagHasAmmoNow"]
end
local unknownBagHasAmmo = false
if bagId and bagHasAmmoNow then
unknownBagHasAmmo = bagHasAmmoNow(bagId) and true or false
end
if bagId and (zButtonAmmo_AmmoBags[bagId] or unknownBagHasAmmo) then
bagIsAmmoRelated = true
end
if bagId and not zButtonAmmo_AmmoBags[bagId] then
if (not unknownBagHasAmmo) and now > 0 and zButtonAmmo_LastFullScanAt > 0 and (now - zButtonAmmo_LastFullScanAt) < zButtonAmmo_MinUnknownBagScanInterval then
zButtonAmmo_UpdateParentCountFast()
return
end
end
end
local minInterval = zButtonAmmo_MinRefreshInterval
if event == "BAG_UPDATE" then
if bagIsAmmoRelated then
minInterval = 0
else
minInterval = zButtonAmmo_MinBagRefreshInterval
end
if UnitAffectingCombat and UnitAffectingCombat("player") then
minInterval = 1.50
if bagIsAmmoRelated then
minInterval = 0
end
end
end
if now > 0 and zButtonAmmo_LastRefreshAt > 0 and (now - zButtonAmmo_LastRefreshAt) < minInterval then
zButtonAmmo_UpdateParentCountFast()
return
end
zButtonAmmo_LastRefreshAt = now
local ammoList = zButtonAmmo_GetAmmoList() local ammoList = zButtonAmmo_GetAmmoList()
local listChanged = (ZHunterMod_Ammo_Buttons ~= ammoList) local listChanged = (ZHunterMod_Ammo_Buttons ~= ammoList)
ZHunterMod_Ammo_Buttons = ammoList ZHunterMod_Ammo_Buttons = ammoList
zButtonAmmo_RebuildCache() zButtonAmmo_RebuildCache()
local cacheSignature = zButtonAmmo_GetCacheSignature() local cacheSignature = zButtonAmmo_GetCacheSignature(ammoList)
local rebuiltButtons = false
if listChanged or cacheSignature ~= zButtonAmmo_LastCacheSignature then if listChanged or cacheSignature ~= zButtonAmmo_LastCacheSignature then
zButtonAmmo_EnsureSpellOrder(ammoList) zButtonAmmo_EnsureSpellOrder(ammoList)
local info = {} local info = {}
@@ -822,15 +1009,10 @@ function zButtonAmmoAdjustment_OnEvent()
end end
zButtonAmmo.found = ZSpellButton_SetButtons(zButtonAmmo, info) zButtonAmmo.found = ZSpellButton_SetButtons(zButtonAmmo, info)
zButtonAmmo_SetupSizeAndPosition() zButtonAmmo_SetupSizeAndPosition()
zButtonAmmo_LastCacheSignature = zButtonAmmo_GetCacheSignature() zButtonAmmo_LastCacheSignature = cacheSignature
end rebuiltButtons = true
-- Update ammo counts for child buttons
for i=1, zButtonAmmo.count do
local button = getglobal("zButtonAmmo"..i)
if button and button.ammoname and not button.isspell then
zButtonAmmo_UpdateButton(button)
end
end end
local equippedAmmoName = zButtonAmmo_GetEquippedAmmoName() local equippedAmmoName = zButtonAmmo_GetEquippedAmmoName()
local equippedInfo = nil local equippedInfo = nil
if equippedAmmoName then if equippedAmmoName then
@@ -839,40 +1021,51 @@ function zButtonAmmoAdjustment_OnEvent()
for i=1, zButtonAmmo.count do for i=1, zButtonAmmo.count do
local child = getglobal("zButtonAmmo"..i) local child = getglobal("zButtonAmmo"..i)
if child and child.ammoname == equippedAmmoName and not child.isspell then if child and child.ammoname == equippedAmmoName and not child.isspell then
equippedInfo = { zButtonAmmo_AssignParentFromChild(child)
ammo = child.ammoname,
brol = child.ammobrol,
quality = child.ammoqual,
lvl = child.ammolvl,
type = child.ammotype,
bag = child.ammobag,
slot = child.ammoslot,
id = child.ammoid or child.id,
link = child.ammolink,
icon = child.icon,
count = child.ammocount,
}
break break
end end
end end
end end
end end
if not equippedInfo then if not equippedInfo and event ~= "BAG_UPDATE" then
equippedInfo = zButtonAmmo_GetEquippedAmmoInfo() equippedInfo = zButtonAmmo_GetEquippedAmmoInfo()
if equippedInfo and equippedInfo.ammo and not equippedAmmoName then if equippedInfo and equippedInfo.ammo and not equippedAmmoName then
equippedAmmoName = equippedInfo.ammo equippedAmmoName = equippedInfo.ammo
end end
end end
if not rebuiltButtons then
for i=1, zButtonAmmo.count do
local child = getglobal("zButtonAmmo"..i)
if child and child.ammoname and not child.isspell then
local cacheInfo = zButtonAmmo_Cache[child.ammoname]
local newCount = cacheInfo and cacheInfo.count or 0
if (child.ammocount or 0) ~= (newCount or 0) then
if cacheInfo then
child.ammobag = cacheInfo.bag
child.ammoslot = cacheInfo.slot
child.ammoqual = cacheInfo.quality
child.ammobrol = cacheInfo.brol
child.ammolvl = cacheInfo.lvl
child.ammotype = cacheInfo.type
child.ammoid = cacheInfo.id
child.ammolink = cacheInfo.link
child.icon = cacheInfo.icon
end
child.ammocount = newCount
zButtonAmmo_UpdateButton(child)
end
end
end
end
if equippedInfo and equippedInfo.ammo and equippedInfo.ammo ~= zButtonAmmo.ammoname then if equippedInfo and equippedInfo.ammo and equippedInfo.ammo ~= zButtonAmmo.ammoname then
zButtonAmmo_AssignButtonFromAmmoInfo(zButtonAmmo, equippedInfo) zButtonAmmo_AssignButtonFromAmmoInfo(zButtonAmmo, equippedInfo)
end end
-- Also update parent if it's showing ammo (not a spell) -- Also update parent if it's showing ammo (not a spell)
if zButtonAmmo and not zButtonAmmo.isspell and zButtonAmmo.ammoname then zButtonAmmo_UpdateParentCountFast()
zButtonAmmo_UpdateButton(zButtonAmmo)
end
-- Save the currently equipped ammo for restoration on next reload -- Save the currently equipped ammo for restoration on next reload
if equippedAmmoName then if equippedAmmoName then
+82 -20
View File
@@ -32,6 +32,12 @@ ZHunterMod_Aspect_Spells = {
} }
local ZHUNTER_ASPECT_MAX = table.getn(ZHunterMod_Aspect_Spells) local ZHUNTER_ASPECT_MAX = table.getn(ZHunterMod_Aspect_Spells)
local zButtonAspect_LastAuraUpdateAt = 0
local zButtonAspect_MinAuraInterval = 0.15
local zButtonAspect_MinAuraIntervalCombat = 0.60
local zButtonAspect_LastAuraTexture = nil
local zButtonAspect_LastParentId = nil
local zButtonAspect_AuraProbeTextLeft1 = nil
local function zButtonAspect_GetSaved() local function zButtonAspect_GetSaved()
local currentRoot = zButtonAspect_GetRoot() local currentRoot = zButtonAspect_GetRoot()
@@ -158,7 +164,13 @@ function zButtonAspect_OnEvent()
zButtonAspectAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED") zButtonAspectAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED")
zButtonAspectAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB") zButtonAspectAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB")
zButtonAspectAdjustment:SetScript("OnEvent", zButtonAspectAdjustment_OnEvent) zButtonAspectAdjustment:SetScript("OnEvent", zButtonAspectAdjustment_OnEvent)
zButtonAspect_Tooltip = CreateFrame("GameTooltip", "zButtonAspect_Tooltip", nil, "GameTooltipTemplate") zButtonAspect_Tooltip = CreateFrame("GameTooltip", "zButtonAspect_AuraProbeScan", nil, "GameTooltipTemplate")
if zButtonAspect_Tooltip and zButtonAspect_Tooltip.GetName then
local tooltipName = zButtonAspect_Tooltip:GetName()
if tooltipName then
zButtonAspect_AuraProbeTextLeft1 = getglobal(tooltipName .. "TextLeft1")
end
end
zButtonAspect_SetupSizeAndPosition() zButtonAspect_SetupSizeAndPosition()
end end
end end
@@ -191,11 +203,17 @@ function zButtonAspect_SetupSizeAndPosition()
zButtonAspect_EnsureConfig() zButtonAspect_EnsureConfig()
local saved = zButtonAspect_GetSaved() local saved = zButtonAspect_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonAspectAdjustment and zButtonAspectAdjustment.SetScript then
zButtonAspectAdjustment:SetScript("OnEvent", nil)
end
if zButtonAspect and zButtonAspect.Hide then if zButtonAspect and zButtonAspect.Hide then
zButtonAspect:Hide() zButtonAspect:Hide()
end end
return return
end end
if zButtonAspectAdjustment and zButtonAspectAdjustment.SetScript then
zButtonAspectAdjustment:SetScript("OnEvent", zButtonAspectAdjustment_OnEvent)
end
local displayCount = zButtonAspect.found or ZHUNTER_ASPECT_MAX local displayCount = zButtonAspect.found or ZHUNTER_ASPECT_MAX
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -234,6 +252,17 @@ function zButtonAspectAdjustment_OnEvent()
if not zButtonAspect or not zButtonAspect.count then if not zButtonAspect or not zButtonAspect.count then
return return
end end
if event == "PLAYER_AURAS_CHANGED" then
local now = GetTime and GetTime() or 0
local minInterval = zButtonAspect_MinAuraInterval
if UnitAffectingCombat and UnitAffectingCombat("player") then
minInterval = zButtonAspect_MinAuraIntervalCombat
end
if now > 0 and zButtonAspect_LastAuraUpdateAt > 0 and (now - zButtonAspect_LastAuraUpdateAt) < minInterval then
return
end
zButtonAspect_LastAuraUpdateAt = now
end
if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
zButtonAspect_CreateButtons() zButtonAspect_CreateButtons()
zButtonAspect_SetupSizeAndPosition() zButtonAspect_SetupSizeAndPosition()
@@ -245,45 +274,78 @@ function zButtonAspectAdjustment_OnEvent()
if not zButtonAspect1.id then if not zButtonAspect1.id then
return return
end end
local buttontextures = {}
local button local button
for i=1, zButtonAspect.count do local isParentTooltipOwned = GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonAspect)
button = getglobal(zButtonAspect.name..i) local childrenShown = zButtonAspect.children and zButtonAspect.children.IsShown and zButtonAspect.children:IsShown()
button.icon = nil local needChildIconScan = (event ~= "PLAYER_AURAS_CHANGED") or childrenShown or isParentTooltipOwned
if button.id then local buttontextures = nil
buttontextures[GetSpellTexture(button.id, "spell")] = button local hasDuplicateChildTexture = nil
if needChildIconScan then
buttontextures = {}
local textureSeen = {}
for i=1, zButtonAspect.count do
button = getglobal(zButtonAspect.name..i)
button.icon = nil
if button.id then
local btnTexture = GetSpellTexture(button.id, "spell")
if btnTexture then
if textureSeen[btnTexture] then
hasDuplicateChildTexture = true
elseif hasDuplicateChildTexture == nil then
hasDuplicateChildTexture = false
end
textureSeen[btnTexture] = 1
buttontextures[btnTexture] = button
end
end
end end
end end
local i = 1 local i = 1
local texture = GetSpellTexture(zButtonAspect1.id, "spell") local texture = GetSpellTexture(zButtonAspect1.id, "spell")
local buff = UnitBuff("player", i) local buff = UnitBuff("player", i)
local spellname, buffname local spellname, buffname
zButtonAspect.id = zButtonAspect1.id local newParentId = zButtonAspect1.id
while buff do while buff do
if texture == buff and zButtonAspect2.id then if texture == buff and zButtonAspect2.id then
zButtonAspect.id = zButtonAspect2.id newParentId = zButtonAspect2.id
end end
if buttontextures[buff] then if needChildIconScan and buttontextures[buff] then
zButtonAspect_Tooltip:SetOwner(this, "ANCHOR_NONE") if hasDuplicateChildTexture then
zButtonAspect_Tooltip:SetUnitBuff("player", i) zButtonAspect_Tooltip:SetOwner(this, "ANCHOR_NONE")
buffname = zButtonAspect_TooltipTextLeft1:GetText() zButtonAspect_Tooltip:SetUnitBuff("player", i)
spellname = GetSpellName(buttontextures[buff].id, "spell") if zButtonAspect_AuraProbeTextLeft1 and zButtonAspect_AuraProbeTextLeft1.GetText then
if buffname == spellname then buffname = zButtonAspect_AuraProbeTextLeft1:GetText()
else
buffname = nil
end
spellname = GetSpellName(buttontextures[buff].id, "spell")
if buffname == spellname then
buttontextures[buff].icon = "Interface\\Icons\\Spell_Nature_WispSplode"
end
else
buttontextures[buff].icon = "Interface\\Icons\\Spell_Nature_WispSplode" buttontextures[buff].icon = "Interface\\Icons\\Spell_Nature_WispSplode"
end end
end end
i = i + 1 i = i + 1
buff = UnitBuff("player", i) buff = UnitBuff("player", i)
end end
if event == "PLAYER_AURAS_CHANGED" and zButtonAspect_LastAuraTexture == texture and zButtonAspect.id == newParentId and zButtonAspect_LastParentId == newParentId and not isParentTooltipOwned then
return
end
zButtonAspect_LastAuraTexture = texture
zButtonAspect_LastParentId = newParentId
zButtonAspect.id = newParentId
ZSpellButton_UpdateButton(zButtonAspect) ZSpellButton_UpdateButton(zButtonAspect)
ZSpellButton_UpdateCooldown(zButtonAspect) ZSpellButton_UpdateCooldown(zButtonAspect)
for i=1, zButtonAspect.count do if needChildIconScan then
button = getglobal(zButtonAspect.name..i) for i=1, zButtonAspect.count do
if button.id then button = getglobal(zButtonAspect.name..i)
ZSpellButton_UpdateButton(button) if button.id then
ZSpellButton_UpdateButton(button)
end
end end
end end
if GameTooltip:IsOwned(zButtonAspect) then if isParentTooltipOwned then
ZSpellButtonParent_OnEnter(zButtonAspect) ZSpellButtonParent_OnEnter(zButtonAspect)
end end
end end
+32
View File
@@ -22,6 +22,20 @@ if not root["zButtonCompanions"] then
end end
local ZHUNTER_COMPANIONS_BUTTON_MAX = 80 local ZHUNTER_COMPANIONS_BUTTON_MAX = 80
local zButtonCompanions_LastSpellSignature = nil
local zButtonCompanions_LastRefreshAt = 0
local zButtonCompanions_MinRefreshInterval = 0.50
local function zButtonCompanions_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
local function zButtonCompanions_GetSaved() local function zButtonCompanions_GetSaved()
local currentRoot = zButtonCompanions_GetRoot() local currentRoot = zButtonCompanions_GetRoot()
@@ -233,11 +247,17 @@ end
function zButtonCompanions_SetupSizeAndPosition() function zButtonCompanions_SetupSizeAndPosition()
local saved = zButtonCompanions_GetSaved() local saved = zButtonCompanions_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonCompanionsAdjustment and zButtonCompanionsAdjustment.SetScript then
zButtonCompanionsAdjustment:SetScript("OnEvent", nil)
end
if zButtonCompanions and zButtonCompanions.Hide then if zButtonCompanions and zButtonCompanions.Hide then
zButtonCompanions:Hide() zButtonCompanions:Hide()
end end
return return
end end
if zButtonCompanionsAdjustment and zButtonCompanionsAdjustment.SetScript then
zButtonCompanionsAdjustment:SetScript("OnEvent", zButtonCompanionsAdjustment_OnEvent)
end
local displayCount = zButtonCompanions.found or 0 local displayCount = zButtonCompanions.found or 0
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -275,6 +295,18 @@ function zButtonCompanionsAdjustment_OnEvent()
if not zButtonCompanions then if not zButtonCompanions then
return return
end end
if event == "SPELLS_CHANGED" then
local now = GetTime and GetTime() or 0
if now > 0 and zButtonCompanions_LastRefreshAt > 0 and (now - zButtonCompanions_LastRefreshAt) < zButtonCompanions_MinRefreshInterval then
return
end
local signature = zButtonCompanions_GetSpellSignature(zButtonCompanions_CollectTabSpellIds())
if signature == zButtonCompanions_LastSpellSignature and not GameTooltip:IsOwned(zButtonCompanions) then
return
end
zButtonCompanions_LastSpellSignature = signature
zButtonCompanions_LastRefreshAt = now
end
zButtonCompanions_RefreshSpells() zButtonCompanions_RefreshSpells()
zButtonCompanions_SetupSizeAndPosition() zButtonCompanions_SetupSizeAndPosition()
if GameTooltip:IsOwned(zButtonCompanions) then if GameTooltip:IsOwned(zButtonCompanions) then
+32
View File
@@ -22,6 +22,20 @@ if not root["zButtonMounts"] then
end end
local ZHUNTER_MOUNTS_BUTTON_MAX = 80 local ZHUNTER_MOUNTS_BUTTON_MAX = 80
local zButtonMounts_LastSpellSignature = nil
local zButtonMounts_LastRefreshAt = 0
local zButtonMounts_MinRefreshInterval = 0.50
local function zButtonMounts_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
local function zButtonMounts_GetSaved() local function zButtonMounts_GetSaved()
local currentRoot = zButtonMounts_GetRoot() local currentRoot = zButtonMounts_GetRoot()
@@ -233,11 +247,17 @@ end
function zButtonMounts_SetupSizeAndPosition() function zButtonMounts_SetupSizeAndPosition()
local saved = zButtonMounts_GetSaved() local saved = zButtonMounts_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonMountsAdjustment and zButtonMountsAdjustment.SetScript then
zButtonMountsAdjustment:SetScript("OnEvent", nil)
end
if zButtonMounts and zButtonMounts.Hide then if zButtonMounts and zButtonMounts.Hide then
zButtonMounts:Hide() zButtonMounts:Hide()
end end
return return
end end
if zButtonMountsAdjustment and zButtonMountsAdjustment.SetScript then
zButtonMountsAdjustment:SetScript("OnEvent", zButtonMountsAdjustment_OnEvent)
end
local displayCount = zButtonMounts.found or 0 local displayCount = zButtonMounts.found or 0
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -275,6 +295,18 @@ function zButtonMountsAdjustment_OnEvent()
if not zButtonMounts then if not zButtonMounts then
return return
end end
if event == "SPELLS_CHANGED" then
local now = GetTime and GetTime() or 0
if now > 0 and zButtonMounts_LastRefreshAt > 0 and (now - zButtonMounts_LastRefreshAt) < zButtonMounts_MinRefreshInterval then
return
end
local signature = zButtonMounts_GetSpellSignature(zButtonMounts_CollectTabSpellIds())
if signature == zButtonMounts_LastSpellSignature and not GameTooltip:IsOwned(zButtonMounts) then
return
end
zButtonMounts_LastSpellSignature = signature
zButtonMounts_LastRefreshAt = now
end
zButtonMounts_RefreshSpells() zButtonMounts_RefreshSpells()
zButtonMounts_SetupSizeAndPosition() zButtonMounts_SetupSizeAndPosition()
if GameTooltip:IsOwned(zButtonMounts) then if GameTooltip:IsOwned(zButtonMounts) then
+83 -18
View File
@@ -43,6 +43,11 @@ ZHunterMod_Pet_Spells = {
local ZHUNTER_PET_MAX = table.getn(ZHunterMod_Pet_Spells) local ZHUNTER_PET_MAX = table.getn(ZHunterMod_Pet_Spells)
local zButtonPet_SetButtons local zButtonPet_SetButtons
local zButtonPet_LastRefreshAt = 0
local zButtonPet_MinRefreshInterval = 0.15
local zButtonPet_MinCombatRefreshInterval = 0.30
local zButtonPet_RuntimeSpellMap = nil
local zButtonPet_LastStateKey = nil
local function zButtonPet_GetSaved() local function zButtonPet_GetSaved()
local currentRoot = zButtonPet_GetRoot() local currentRoot = zButtonPet_GetRoot()
@@ -134,6 +139,40 @@ local function zButtonPet_ApplyRuntimeSettings()
zButtonPet.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false zButtonPet.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false
end end
local function zButtonPet_RebuildRuntimeSpellMap()
if not zButtonPet or not zButtonPet.count then
zButtonPet_RuntimeSpellMap = {}
return zButtonPet_RuntimeSpellMap
end
local spells = {}
for i=1, zButtonPet.count do
local button = getglobal(zButtonPet.name..i)
if button and button.id then
local spellName = GetSpellName(button.id, "spell")
if spellName and spellName ~= "" then
spells[spellName] = button.id
end
end
end
zButtonPet_RuntimeSpellMap = spells
return spells
end
local function zButtonPet_GetPreferredDefaultParentId(spells)
if zButtonPet1 and zButtonPet1.id then
return zButtonPet1.id
end
if zButtonPet and zButtonPet.count then
for i = 1, zButtonPet.count do
local child = getglobal("zButtonPet" .. i)
if child and child.id then
return child.id
end
end
end
return spells[ZHUNTER_PET_EYES] or spells[ZHUNTER_PET_POSITION] or spells[ZHUNTER_PET_DISMISS]
end
function zButtonPet_OnLoad() function zButtonPet_OnLoad()
this:RegisterEvent("VARIABLES_LOADED") this:RegisterEvent("VARIABLES_LOADED")
end end
@@ -161,7 +200,6 @@ function zButtonPet_OnEvent()
zButtonPetAdjustment:RegisterEvent("SPELLS_CHANGED") zButtonPetAdjustment:RegisterEvent("SPELLS_CHANGED")
zButtonPetAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB") zButtonPetAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB")
zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent) zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent)
zButtonPet_Tooltip = CreateFrame("GameTooltip", "zButtonPet_Tooltip", nil, "GameTooltipTemplate")
zButtonPet_SetupSizeAndPosition() zButtonPet_SetupSizeAndPosition()
zButtonPetAdjustment_OnEvent() zButtonPetAdjustment_OnEvent()
end end
@@ -188,16 +226,24 @@ function zButtonPet_CreateButtons()
end end
zButtonPet.found = ZSpellButton_SetButtons(zButtonPet, info) zButtonPet.found = ZSpellButton_SetButtons(zButtonPet, info)
zButtonPet_ApplyRuntimeSettings() zButtonPet_ApplyRuntimeSettings()
zButtonPet_RebuildRuntimeSpellMap()
zButtonPet_LastStateKey = nil
end end
function zButtonPet_SetupSizeAndPosition() function zButtonPet_SetupSizeAndPosition()
local saved = zButtonPet_GetSaved() local saved = zButtonPet_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonPetAdjustment and zButtonPetAdjustment.SetScript then
zButtonPetAdjustment:SetScript("OnEvent", nil)
end
if zButtonPet and zButtonPet.Hide then if zButtonPet and zButtonPet.Hide then
zButtonPet:Hide() zButtonPet:Hide()
end end
return return
end end
if zButtonPetAdjustment and zButtonPetAdjustment.SetScript then
zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent)
end
local displayCount = zButtonPet.found or ZHUNTER_PET_MAX local displayCount = zButtonPet.found or ZHUNTER_PET_MAX
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -434,20 +480,12 @@ local function zButtonPet_RefreshParentSpell()
zButtonPet_RefreshSavedPetState(saved, corePetInfo) zButtonPet_RefreshSavedPetState(saved, corePetInfo)
local status, happiness, dead local status, happiness, dead
local spells = {} local spells = zButtonPet_RuntimeSpellMap
local name if not spells then
local choice spells = zButtonPet_RebuildRuntimeSpellMap()
for i=1, zButtonPet.count do
local button
button = getglobal(zButtonPet.name..i)
if button and button.id then
name = GetSpellName(button.id, "spell")
spells[name] = button
if not choice and (name == ZHUNTER_PET_DISMISS or name == ZHUNTER_PET_EYES or name == ZHUNTER_PET_POSITION) then
choice = button
end
end
end end
local name
local choiceId = zButtonPet_GetPreferredDefaultParentId(spells)
status = saved["pet"]["status"] status = saved["pet"]["status"]
happiness = saved["pet"]["happiness"] happiness = saved["pet"]["happiness"]
dead = saved["pet"]["dead"] dead = saved["pet"]["dead"]
@@ -464,22 +502,34 @@ local function zButtonPet_RefreshParentSpell()
name = ZHUNTER_PET_MEND name = ZHUNTER_PET_MEND
elseif happiness ~= 3 then elseif happiness ~= 3 then
name = ZHUNTER_PET_FEED name = ZHUNTER_PET_FEED
elseif choice then elseif choiceId then
id = choice.id id = choiceId
end end
if name and spells[name] then if name and spells[name] then
id = spells[name].id id = spells[name]
elseif name then elseif name then
local fallbackId = zButtonPet_FindSpellIdByName(name) local fallbackId = zButtonPet_FindSpellIdByName(name)
if fallbackId then if fallbackId then
spells[name] = fallbackId
id = fallbackId id = fallbackId
end end
end end
local isOwned = GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonPet)
local stateKey = tostring(hasCurrentPet and 1 or 0)
..":"..tostring(hasLivePet and 1 or 0)
..":"..tostring(status or "")
..":"..tostring(happiness or "")
..":"..tostring(dead or 0)
..":"..tostring(id or 0)
if zButtonPet.id == id and zButtonPet_LastStateKey == stateKey and not isOwned then
return
end
zButtonPet_LastStateKey = stateKey
zButtonPet.id = id zButtonPet.id = id
ZSpellButton_UpdateButton(zButtonPet) ZSpellButton_UpdateButton(zButtonPet)
ZSpellButton_UpdateCooldown(zButtonPet) ZSpellButton_UpdateCooldown(zButtonPet)
if GameTooltip:IsOwned(zButtonPet) then if isOwned then
ZSpellButtonParent_OnEnter(zButtonPet) ZSpellButtonParent_OnEnter(zButtonPet)
end end
end end
@@ -503,6 +553,21 @@ function zButtonPetAdjustment_OnEvent()
if event == "UNIT_PET" and arg1 and arg1 ~= "player" then if event == "UNIT_PET" and arg1 and arg1 ~= "player" then
return return
end end
if event == "SPELLS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
zButtonPet_CreateButtons()
zButtonPet_SetupSizeAndPosition()
end
if event == "UNIT_HEALTH" or event == "UNIT_HAPPINESS" or event == "PET_BAR_UPDATE" then
local now = GetTime and GetTime() or 0
local minInterval = zButtonPet_MinRefreshInterval
if UnitAffectingCombat and UnitAffectingCombat("player") then
minInterval = zButtonPet_MinCombatRefreshInterval
end
if now > 0 and zButtonPet_LastRefreshAt > 0 and (now - zButtonPet_LastRefreshAt) < minInterval then
return
end
zButtonPet_LastRefreshAt = now
end
zButtonPet_RefreshParentSpell() zButtonPet_RefreshParentSpell()
end end
+47 -2
View File
@@ -27,6 +27,11 @@ ZHunterMod_Ranged_Weapons = ZHunterMod_Ranged_Weapons or {}
local ZHUNTER_RANGED_MAX_COUNT = 80 local ZHUNTER_RANGED_MAX_COUNT = 80
local zButtonRanged_Cache = {} local zButtonRanged_Cache = {}
local zButtonRanged_TooltipWarmed = false local zButtonRanged_TooltipWarmed = false
local zButtonRanged_LastRefreshAt = 0
local zButtonRanged_MinBagRefreshInterval = 0.75
local zButtonRanged_IdleBagRefreshInterval = 3.00
local zButtonRanged_BagDirty = false
local zButtonRanged_RefreshButtonsFromState
local function zButtonRanged_GetKnownItemSpeed(itemId) local function zButtonRanged_GetKnownItemSpeed(itemId)
local idNum = tonumber(itemId) local idNum = tonumber(itemId)
@@ -437,6 +442,15 @@ local function zButtonRanged_OnEnter(button)
if not button then if not button then
return return
end end
if zButtonRanged_BagDirty then
local previousFound = tonumber(zButtonRanged and zButtonRanged.found) or 0
zButtonRanged_RefreshButtonsFromState()
if (tonumber(zButtonRanged and zButtonRanged.found) or 0) ~= previousFound then
zButtonRanged_SetupSizeAndPosition()
else
zButtonRanged_UpdateButton(zButtonRanged)
end
end
if not (zButtonRanged and zButtonRanged.tooltip) then if not (zButtonRanged and zButtonRanged.tooltip) then
return return
end end
@@ -503,7 +517,7 @@ local function zButtonRanged_BeforeClick(button)
return true return true
end end
local function zButtonRanged_RefreshButtonsFromState() zButtonRanged_RefreshButtonsFromState = function()
if not zButtonRanged then if not zButtonRanged then
return return
end end
@@ -525,6 +539,7 @@ local function zButtonRanged_RefreshButtonsFromState()
end end
zButtonRanged.found = ZSpellButton_SetButtons(zButtonRanged, info) zButtonRanged.found = ZSpellButton_SetButtons(zButtonRanged, info)
zButtonRanged_BagDirty = false
zButtonRanged_ApplyRuntimeSettings() zButtonRanged_ApplyRuntimeSettings()
end end
@@ -620,11 +635,17 @@ end
function zButtonRanged_SetupSizeAndPosition() function zButtonRanged_SetupSizeAndPosition()
local saved = zButtonRanged_GetSaved() local saved = zButtonRanged_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonRangedAdjustment and zButtonRangedAdjustment.SetScript then
zButtonRangedAdjustment:SetScript("OnEvent", nil)
end
if zButtonRanged and zButtonRanged.Hide then if zButtonRanged and zButtonRanged.Hide then
zButtonRanged:Hide() zButtonRanged:Hide()
end end
return return
end end
if zButtonRangedAdjustment and zButtonRangedAdjustment.SetScript then
zButtonRangedAdjustment:SetScript("OnEvent", zButtonRangedAdjustment_OnEvent)
end
local displayCount = zButtonRanged.found or 0 local displayCount = zButtonRanged.found or 0
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -665,8 +686,32 @@ function zButtonRangedAdjustment_OnEvent()
if event == "UNIT_INVENTORY_CHANGED" and arg1 and arg1 ~= "player" then if event == "UNIT_INVENTORY_CHANGED" and arg1 and arg1 ~= "player" then
return return
end end
if event == "BAG_UPDATE" then
local now = GetTime and GetTime() or 0
local isInspecting = false
if zButtonRanged.children and zButtonRanged.children.IsShown and zButtonRanged.children:IsShown() then
isInspecting = true
elseif GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonRanged) then
isInspecting = true
end
if not isInspecting then
zButtonRanged_BagDirty = true
return
end
local minInterval = isInspecting and zButtonRanged_MinBagRefreshInterval or zButtonRanged_IdleBagRefreshInterval
if now > 0 and zButtonRanged_LastRefreshAt > 0 and (now - zButtonRanged_LastRefreshAt) < minInterval then
return
end
end
zButtonRanged_LastRefreshAt = GetTime and GetTime() or 0
local previousFound = tonumber(zButtonRanged.found) or 0
zButtonRanged_RefreshButtonsFromState() zButtonRanged_RefreshButtonsFromState()
zButtonRanged_SetupSizeAndPosition() if (tonumber(zButtonRanged.found) or 0) ~= previousFound then
zButtonRanged_SetupSizeAndPosition()
else
zButtonRanged_UpdateButton(zButtonRanged)
end
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
zButtonRanged_PrewarmTooltipOnce() zButtonRanged_PrewarmTooltipOnce()
end end
+32
View File
@@ -22,6 +22,20 @@ if not root["zButtonToys"] then
end end
local ZHUNTER_TOYS_BUTTON_MAX = 80 local ZHUNTER_TOYS_BUTTON_MAX = 80
local zButtonToys_LastSpellSignature = nil
local zButtonToys_LastRefreshAt = 0
local zButtonToys_MinRefreshInterval = 0.50
local function zButtonToys_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
local function zButtonToys_GetSaved() local function zButtonToys_GetSaved()
local currentRoot = zButtonToys_GetRoot() local currentRoot = zButtonToys_GetRoot()
@@ -233,11 +247,17 @@ end
function zButtonToys_SetupSizeAndPosition() function zButtonToys_SetupSizeAndPosition()
local saved = zButtonToys_GetSaved() local saved = zButtonToys_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonToysAdjustment and zButtonToysAdjustment.SetScript then
zButtonToysAdjustment:SetScript("OnEvent", nil)
end
if zButtonToys and zButtonToys.Hide then if zButtonToys and zButtonToys.Hide then
zButtonToys:Hide() zButtonToys:Hide()
end end
return return
end end
if zButtonToysAdjustment and zButtonToysAdjustment.SetScript then
zButtonToysAdjustment:SetScript("OnEvent", zButtonToysAdjustment_OnEvent)
end
local displayCount = zButtonToys.found or 0 local displayCount = zButtonToys.found or 0
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -275,6 +295,18 @@ function zButtonToysAdjustment_OnEvent()
if not zButtonToys then if not zButtonToys then
return return
end end
if event == "SPELLS_CHANGED" then
local now = GetTime and GetTime() or 0
if now > 0 and zButtonToys_LastRefreshAt > 0 and (now - zButtonToys_LastRefreshAt) < zButtonToys_MinRefreshInterval then
return
end
local signature = zButtonToys_GetSpellSignature(zButtonToys_CollectTabSpellIds())
if signature == zButtonToys_LastSpellSignature and not GameTooltip:IsOwned(zButtonToys) then
return
end
zButtonToys_LastSpellSignature = signature
zButtonToys_LastRefreshAt = now
end
zButtonToys_RefreshSpells() zButtonToys_RefreshSpells()
zButtonToys_SetupSizeAndPosition() zButtonToys_SetupSizeAndPosition()
if GameTooltip:IsOwned(zButtonToys) then if GameTooltip:IsOwned(zButtonToys) then
+169 -20
View File
@@ -36,8 +36,159 @@ ZHunterMod_Track_Spells = {
} }
local ZHUNTER_TRACK_MAX = table.getn(ZHunterMod_Track_Spells) local ZHUNTER_TRACK_MAX = table.getn(ZHunterMod_Track_Spells)
local zButtonTrack_LastTrackingTexture = nil
local zButtonTrack_SyncActiveChild
local zButtonTrack_GetSaved
local function zButtonTrack_GetSaved() local function zButtonTrack_NormalizeTexture(texture)
if not texture then
return ""
end
local value = string.lower(tostring(texture))
value = string.gsub(value, "\\", "/")
local _, _, basename = string.find(value, "([^/]+)$")
return basename or value
end
local function zButtonTrack_FindSpellIdByTrackingTexture(trackingTexture)
if not trackingTexture or not zButtonTrack or not zButtonTrack.count then
return nil
end
local wanted = zButtonTrack_NormalizeTexture(trackingTexture)
for i = 1, zButtonTrack.count do
local child = getglobal("zButtonTrack" .. i)
if child and child.id then
local spellTexture = GetSpellTexture and GetSpellTexture(child.id, "spell") or nil
if zButtonTrack_NormalizeTexture(spellTexture) == wanted then
return child.id
end
if child.icon and zButtonTrack_NormalizeTexture(child.icon) == wanted then
return child.id
end
if child.icontexture and child.icontexture.GetTexture then
local childTexture = child.icontexture:GetTexture()
if zButtonTrack_NormalizeTexture(childTexture) == wanted then
return child.id
end
end
end
end
return nil
end
local function zButtonTrack_FindSpellIdByTrackingName(trackingName)
if not trackingName or trackingName == "" or not zButtonTrack or not zButtonTrack.count then
return nil
end
for i = 1, zButtonTrack.count do
local child = getglobal("zButtonTrack" .. i)
if child and child.id then
local spellName = GetSpellName and GetSpellName(child.id, "spell") or nil
if spellName == trackingName then
return child.id
end
end
end
return nil
end
local function zButtonTrack_GetActiveTrackingSpellId()
local activeName = nil
local activeTexture = nil
if GetNumTrackingTypes and GetTrackingInfo then
local numTracking = GetNumTrackingTypes() or 0
for i = 1, numTracking do
local name, texture, active = GetTrackingInfo(i)
if active then
activeName = name
activeTexture = texture
break
end
end
end
local byName = zButtonTrack_FindSpellIdByTrackingName(activeName)
if byName then
return byName, activeTexture
end
if activeTexture then
return zButtonTrack_FindSpellIdByTrackingTexture(activeTexture), activeTexture
end
local buff = GetTrackingTexture and GetTrackingTexture() or nil
return zButtonTrack_FindSpellIdByTrackingTexture(buff), buff
end
local function zButtonTrack_AfterClick(button)
if not zButtonTrack then
return
end
if button and button.id then
zButtonTrack.id = button.id
ZSpellButton_UpdateButton(zButtonTrack)
ZSpellButton_UpdateCooldown(zButtonTrack)
zButtonTrack_SyncActiveChild(button.id)
if GetSpellTexture then
zButtonTrack_LastTrackingTexture = GetSpellTexture(button.id, "spell") or zButtonTrack_LastTrackingTexture
end
local isOwned = GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonTrack)
if isOwned then
ZSpellButtonParent_OnEnter(zButtonTrack)
end
end
end
zButtonTrack_SyncActiveChild = function(activeId)
if not zButtonTrack or not zButtonTrack.count then
return
end
for i = 1, zButtonTrack.count do
local child = getglobal("zButtonTrack" .. i)
if child and child.SetChecked then
child:SetChecked((activeId and child.id == activeId) and 1 or 0)
end
end
if zButtonTrack.SetChecked then
zButtonTrack:SetChecked(0)
end
end
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)
end
return
end
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
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
end
zButtonTrack_GetSaved = function()
local currentRoot = zButtonTrack_GetRoot() local currentRoot = zButtonTrack_GetRoot()
if not currentRoot["zButtonTrack"] then if not currentRoot["zButtonTrack"] then
currentRoot["zButtonTrack"] = {} currentRoot["zButtonTrack"] = {}
@@ -119,11 +270,13 @@ function zButtonTrack_OnEvent()
return return
end end
zButtonTrack_CreateButtons() zButtonTrack_CreateButtons()
zButtonTrack.afterclick = zButtonTrack_AfterClick
zButtonTrackAdjustment = CreateFrame("Frame", "zButtonTrackAdjustment") zButtonTrackAdjustment = CreateFrame("Frame", "zButtonTrackAdjustment")
zButtonTrackAdjustment:RegisterEvent("PLAYER_AURAS_CHANGED") zButtonTrackAdjustment:RegisterEvent("MINIMAP_UPDATE_TRACKING")
zButtonTrackAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") zButtonTrackAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD")
zButtonTrackAdjustment:RegisterEvent("SPELLS_CHANGED") zButtonTrackAdjustment:RegisterEvent("SPELLS_CHANGED")
zButtonTrackAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED") zButtonTrackAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED")
zButtonTrackAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB")
zButtonTrackAdjustment:SetScript("OnEvent", zButtonTrackAdjustment_OnEvent) zButtonTrackAdjustment:SetScript("OnEvent", zButtonTrackAdjustment_OnEvent)
zButtonTrack_SetupSizeAndPosition() zButtonTrack_SetupSizeAndPosition()
end end
@@ -156,11 +309,17 @@ function zButtonTrack_SetupSizeAndPosition()
zButtonTrack_EnsureConfig() zButtonTrack_EnsureConfig()
local saved = zButtonTrack_GetSaved() local saved = zButtonTrack_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonTrackAdjustment and zButtonTrackAdjustment.SetScript then
zButtonTrackAdjustment:SetScript("OnEvent", nil)
end
if zButtonTrack and zButtonTrack.Hide then if zButtonTrack and zButtonTrack.Hide then
zButtonTrack:Hide() zButtonTrack:Hide()
end end
return return
end end
if zButtonTrackAdjustment and zButtonTrackAdjustment.SetScript then
zButtonTrackAdjustment:SetScript("OnEvent", zButtonTrackAdjustment_OnEvent)
end
local displayCount = zButtonTrack.found or ZHUNTER_TRACK_MAX local displayCount = zButtonTrack.found or ZHUNTER_TRACK_MAX
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -171,6 +330,7 @@ function zButtonTrack_SetupSizeAndPosition()
ZSpellButton_ArrangeChildren(zButtonTrack, saved["rows"], ZSpellButton_ArrangeChildren(zButtonTrack, saved["rows"],
displayCount, saved["horizontal"], displayCount, saved["horizontal"],
saved["vertical"]) saved["vertical"])
zButtonTrack_RefreshTrackingState()
end end
function zButtonTrack_Reset() function zButtonTrack_Reset()
@@ -199,25 +359,13 @@ function zButtonTrackAdjustment_OnEvent()
if not zButtonTrack or not zButtonTrack.count then if not zButtonTrack or not zButtonTrack.count then
return return
end end
if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
zButtonTrack_CreateButtons() zButtonTrack_CreateButtons()
zButtonTrack_SetupSizeAndPosition() zButtonTrack_SetupSizeAndPosition()
end end
if event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_ENTERING_WORLD" if event == "MINIMAP_UPDATE_TRACKING" or event == "PLAYER_ENTERING_WORLD"
or event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then or event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
local i = 1 zButtonTrack_RefreshTrackingState()
local texture = zButtonTrack1IconTexture:GetTexture()
local buff = GetTrackingTexture()
if texture == buff and zButtonTrack2.id then
zButtonTrack.id = zButtonTrack2.id
else
zButtonTrack.id = zButtonTrack1.id
end
ZSpellButton_UpdateButton(zButtonTrack)
ZSpellButton_UpdateCooldown(zButtonTrack)
if GameTooltip:IsOwned(zButtonTrack) then
ZSpellButtonParent_OnEnter(zButtonTrack)
end
end end
end end
@@ -229,9 +377,9 @@ function zButtonTrack_KeyBinding(index)
local button local button
if index then if index then
button = getglobal("zButtonTrack"..index) button = getglobal("zButtonTrack"..index)
else else
button = zButtonTrack button = zButtonTrack
end end
if button.id then if button.id then
CastSpell(button.id, "spell") CastSpell(button.id, "spell")
if zButtonTrack.hideonclick then if zButtonTrack.hideonclick then
@@ -249,6 +397,7 @@ SlashCmdList["zButtonTrack"] = function(msg)
if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Track button is disabled while module 'zhunter' is disabled.") then if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Track button is disabled while module 'zhunter' is disabled.") then
return return
end end
msg = tostring(msg or "")
if msg == "reset" then if msg == "reset" then
zButtonTrack_Reset() zButtonTrack_Reset()
zButtonTrack:ClearAllPoints() zButtonTrack:ClearAllPoints()
+17 -1
View File
@@ -29,6 +29,8 @@ ZHunterMod_Trap_Spells = {
} }
local ZHUNTER_TRAP_MAX = table.getn(ZHunterMod_Trap_Spells) local ZHUNTER_TRAP_MAX = table.getn(ZHunterMod_Trap_Spells)
local zButtonTrap_LastParentId = nil
local zButtonTrap_LastCombatState = nil
local function zButtonTrap_GetSaved() local function zButtonTrap_GetSaved()
local currentRoot = zButtonTrap_GetRoot() local currentRoot = zButtonTrap_GetRoot()
@@ -150,11 +152,17 @@ function zButtonTrap_SetupSizeAndPosition()
zButtonTrap_EnsureConfig() zButtonTrap_EnsureConfig()
local saved = zButtonTrap_GetSaved() local saved = zButtonTrap_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then if saved["enabled"] == false or saved["enabled"] == 0 then
if zButtonTrapAdjustment and zButtonTrapAdjustment.SetScript then
zButtonTrapAdjustment:SetScript("OnEvent", nil)
end
if zButtonTrap and zButtonTrap.Hide then if zButtonTrap and zButtonTrap.Hide then
zButtonTrap:Hide() zButtonTrap:Hide()
end end
return return
end end
if zButtonTrapAdjustment and zButtonTrapAdjustment.SetScript then
zButtonTrapAdjustment:SetScript("OnEvent", zButtonTrapAdjustment_OnEvent)
end
local displayCount = zButtonTrap.found or ZHUNTER_TRAP_MAX local displayCount = zButtonTrap.found or ZHUNTER_TRAP_MAX
if displayCount < 0 then if displayCount < 0 then
displayCount = 0 displayCount = 0
@@ -193,9 +201,11 @@ function zButtonTrapAdjustment_OnEvent()
if not zButtonTrap or not zButtonTrap.count then if not zButtonTrap or not zButtonTrap.count then
return return
end end
local rebuild = false
if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then
zButtonTrap_CreateButtons() zButtonTrap_CreateButtons()
zButtonTrap_SetupSizeAndPosition() zButtonTrap_SetupSizeAndPosition()
rebuild = true
end end
local nextbutton local nextbutton
local button local button
@@ -215,7 +225,13 @@ function zButtonTrapAdjustment_OnEvent()
end end
end end
end end
zButtonTrap.id = nextbutton and nextbutton.id or zButtonTrap1.id local newParentId = nextbutton and nextbutton.id or zButtonTrap1.id
if (not rebuild) and zButtonTrap_LastParentId == newParentId and zButtonTrap_LastCombatState == combat and not GameTooltip:IsOwned(zButtonTrap) then
return
end
zButtonTrap_LastParentId = newParentId
zButtonTrap_LastCombatState = combat
zButtonTrap.id = newParentId
ZSpellButton_UpdateButton(zButtonTrap) ZSpellButton_UpdateButton(zButtonTrap)
ZSpellButton_UpdateCooldown(zButtonTrap) ZSpellButton_UpdateCooldown(zButtonTrap)
if GameTooltip:IsOwned(zButtonTrap) then if GameTooltip:IsOwned(zButtonTrap) then