This commit is contained in:
DuvelCorp
2026-03-01 09:10:22 +01:00
parent 6b82d0ca46
commit 1a28d95b87
36 changed files with 1671 additions and 325 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
MTH_CONST = MTH_CONST or {}
MTH_CONST.version = MTH_CONST.version or "1.0.4"
MTH_CONST.version = MTH_CONST.version or "1.0.5"
MTH_CONST.WEAPON_TYPES = {
BOWS = "Bows",
+6 -3
View File
@@ -526,6 +526,7 @@ local function MTH_FEED_CoreDropItemOnUnitHook(unit)
end
if target == "pet" and CursorHasItem and CursorHasItem() then
MTH_FEED_EnsureTrackerFrame()
local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot()
local pet = MTH_FEED_GetCurrentPetContext()
if not itemId then
@@ -608,7 +609,6 @@ function MTH_FEED_DebugStatus()
end
function MTH_FEED_ReinstallCoreTracking()
MTH_FEED_EnsureTrackerFrame()
local ok = MTH_FEED_InstallCoreHooks()
return ok and true or false
end
@@ -624,8 +624,12 @@ local function MTH_FEED_EnsureBootstrapFrame()
frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:SetScript("OnEvent", function()
frame:SetScript("OnEvent", function(self)
MTH_FEED_ReinstallCoreTracking()
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
end
end)
MTH_FEED_BootstrapFrame = frame
return frame
@@ -1542,6 +1546,5 @@ function MTH_FEED_DebugDumpCurrentPet(limit)
return true
end
MTH_FEED_EnsureTrackerFrame()
MTH_FEED_InstallCoreHooks()
MTH_FEED_EnsureBootstrapFrame()
+50 -44
View File
@@ -1,5 +1,5 @@
MTH = MTH or {
version = "1.0.4",
version = "1.0.5",
name = "MetaHunt",
modules = {},
config = {},
@@ -424,9 +424,37 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
local resolvedName = MTH_NormalizeModuleName(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
MTH_ModuleStateDebug("read " .. tostring(name) .. " no-savedvars fallback-default=" .. tostring(defaultEnabled and true or false))
return defaultEnabled and true or false
MTH_SavedVariables = {}
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
if type(MTH_SavedVariables.moduleStates) == "table" then
@@ -435,25 +463,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
if MTH_SavedVariables.moduleStates[key] ~= nil then
local resolved = MTH_SavedVariables.moduleStates[key] and true or false
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
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
MTH_CharSavedVariables.modules[resolvedName] = {}
end
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
return resolved
end
end
@@ -465,9 +479,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
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
MTH_ModuleStateDebug("read " .. tostring(name) .. " from modules." .. tostring(key) .. ".enabled=" .. tostring(resolved))
if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then
MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables.modules[key]
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
end
end
@@ -478,16 +494,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
if type(MTH_SavedVariables[key]) == "table" and MTH_SavedVariables[key].enabled ~= nil then
local resolved = MTH_SavedVariables[key].enabled and true or false
MTH_ModuleStateDebug("read " .. tostring(name) .. " from legacy-top-level." .. tostring(key) .. ".enabled=" .. tostring(resolved))
if type(MTH_SavedVariables.modules) ~= "table" then
MTH_SavedVariables.modules = {}
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
MTH_CharSavedVariables.modules[resolvedName] = {}
end
if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then
MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables[key]
end
if type(MTH_SavedVariables.moduleStates) ~= "table" then
MTH_SavedVariables.moduleStates = {}
end
MTH_SavedVariables.moduleStates[resolvedName] = resolved
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
return resolved
end
end
@@ -506,27 +517,22 @@ local function MTH_SetPersistedModuleEnabled(name, enabled)
if type(MTH_SavedVariables) ~= "table" then
MTH_SavedVariables = {}
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
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_CharSavedVariables.modules[name]) ~= "table" then
MTH_CharSavedVariables.modules[name] = {}
end
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.modules[name].enabled = enabled
MTH_ModuleStateDebug("write " .. tostring(name) .. " enabled=" .. tostring(enabled))
end
+34
View File
@@ -20,6 +20,7 @@ end
local MTH_PT_ApplyScan
local MTH_PT_AbilityHasPositiveRanks
local MTH_PT_PromoteUnrankedKnownAliases
local MTH_PT_BootstrapFrame = nil
local MTH_PT_VERBOSE_SCAN_LOGS = false
@@ -846,6 +847,38 @@ function MTH_PT_InitService()
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)
if not MTH_PetTrainingFrame then
return
@@ -929,3 +962,4 @@ end
MTH_TR_InitService = MTH_PT_InitService
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_ForceScanUntil = 0
local MTH_PS_ForceScanBudget = 0
local MTH_PS_EventThrottle = {}
local function MTH_PS_ClearForceWindow()
MTH_PS_ForceScanUntil = 0
@@ -26,9 +27,25 @@ end
local function MTH_PS_IsFollowupEventTrigger(trigger)
local t = tostring(trigger or "")
return t == "event:UNIT_PET"
or t == "UNIT_PET"
or t == "event:PET_BAR_UPDATE"
or t == "PET_BAR_UPDATE"
or t == "event:SPELLS_CHANGED"
or t == "SPELLS_CHANGED"
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
local function MTH_PS_IsPriorityTrigger(trigger)
@@ -237,7 +254,19 @@ local function MTH_PS_OnEvent(_, evt, eventArg1)
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
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
+86 -19
View File
@@ -8,6 +8,7 @@ local MTH_PETS_TRACE_RUNAWAY = false
MTH_PETS_TRACE_CONSISTENCY = false
local MTH_StableFrame = nil
local MTH_ST_BootstrapFrame = nil
local MTH_ST_LastAutoScanAt = 0
local MTH_ST_LastUnitPetHadPet = false
local MTH_PETS_LastTameAttempt = nil
@@ -15,6 +16,8 @@ local MTH_PETS_LiveState = nil
local MTH_PETS_LiveStateSeq = 0
local MTH_PETS_LiveStateSubscribers = {}
local MTH_PETS_LiveStateEventName = "MTH_PET_LIVE_STATE_CHANGED"
local MTH_PETS_EventThrottle = {}
local MTH_PETS_TRACE_TAME = false
MTH_PETS_CoreOriginal_PetRename = MTH_PETS_CoreOriginal_PetRename or nil
@@ -61,6 +64,17 @@ local function MTH_PETS_RequestPetSpellScan(reason)
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)
if type(state) ~= "table" then
return nil
@@ -146,7 +160,10 @@ local function MTH_PETS_NotifyLiveSubscribers(state, source)
end
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 changed = force and true or (not MTH_PETS_AreLiveStatesEqual(MTH_PETS_LiveState, nextState))
if not changed then
@@ -2784,35 +2801,45 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
local pets = MTH_PETS_GetRootStore()
MTH_PETS_MaybePromptStableVisit(pets)
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PLAYER_ENTERING_WORLD", true)
MTH_PETS_EmitLiveState("PLAYER_ENTERING_WORLD", true)
return
end
if evt == "SPELLCAST_CHANNEL_START" then
MTH_PETS_RecordTameAttempt("event:" .. tostring(evt), eventArg1)
MTH_PETS_RecordTameAttempt(evt, eventArg1)
return
end
if evt == "SPELLCAST_STOP" or evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then
MTH_PETS_LogTame("Spell terminal evt=" .. tostring(evt) .. " spell='" .. tostring(eventArg1 or "") .. "' pending=" .. tostring(type(MTH_PETS_LastTameAttempt) == "table"))
if MTH_PETS_TRACE_TAME then
MTH_PETS_LogTame("Spell terminal evt=" .. tostring(evt) .. " spell='" .. tostring(eventArg1 or "") .. "' pending=" .. tostring(type(MTH_PETS_LastTameAttempt) == "table"))
end
local terminalIsTame = MTH_PETS_IsTameBeastSpellName(eventArg1)
if not terminalIsTame and type(MTH_PETS_LastTameAttempt) == "table" then
terminalIsTame = true
MTH_PETS_LogTame("Treating terminal event as tame because pending attempt exists")
if MTH_PETS_TRACE_TAME then
MTH_PETS_LogTame("Treating terminal event as tame because pending attempt exists")
end
end
if not terminalIsTame then
local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName()
if fallbackName and MTH_PETS_IsTameBeastSpellName(fallbackName) then
terminalIsTame = true
MTH_PETS_LogTame("Resolved terminal spell via " .. tostring(fallbackSource) .. " => '" .. tostring(fallbackName) .. "'")
if MTH_PETS_TRACE_TAME then
MTH_PETS_LogTame("Resolved terminal spell via " .. tostring(fallbackSource) .. " => '" .. tostring(fallbackName) .. "'")
end
end
end
if terminalIsTame then
if evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then
MTH_PETS_LogTame("Tame ended with failure/interruption; clearing pending attempt")
if MTH_PETS_TRACE_TAME then
MTH_PETS_LogTame("Tame ended with failure/interruption; clearing pending attempt")
end
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")
if MTH_PETS_TRACE_TAME then
MTH_PETS_LogTame("Tame channel ended; awaiting UNIT_PET to confirm acquire")
end
end
end
return
@@ -2822,14 +2849,17 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
if eventArg1 ~= "player" then
return
end
MTH_PETS_HandleUnitPetTransition("event:UNIT_PET")
MTH_PETS_EmitLiveState("event:UNIT_PET")
MTH_PETS_HandleUnitPetTransition("UNIT_PET")
MTH_PETS_EmitLiveState("UNIT_PET")
return
end
if evt == "PET_BAR_UPDATE" then
if not MTH_PETS_ShouldHandleThrottledEvent("PET_BAR_UPDATE", 0.5) then
return
end
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_BAR_UPDATE")
MTH_PETS_EmitLiveState("PET_BAR_UPDATE")
return
end
@@ -2837,14 +2867,20 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
if eventArg1 and eventArg1 ~= "pet" then
return
end
if not MTH_PETS_ShouldHandleThrottledEvent("UNIT_HAPPINESS", 0.75) then
return
end
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:UNIT_HAPPINESS")
MTH_PETS_EmitLiveState("UNIT_HAPPINESS")
return
end
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_EmitLiveState("event:" .. tostring(evt))
MTH_PETS_EmitLiveState(evt)
return
end
@@ -2852,28 +2888,28 @@ local function MTH_ST_OnEvent(_, evt, eventArg1)
local matchedRunaway = MTH_PETS_IsRunawaySystemMessage(eventArg1)
MTH_PETS_TraceRunawayEvent(evt, eventArg1, matchedRunaway)
if matchedRunaway then
MTH_PETS_RecordPetRunaway("event:" .. tostring(evt), eventArg1)
MTH_PETS_RecordPetRunaway(evt, eventArg1)
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:" .. tostring(evt))
MTH_PETS_EmitLiveState(evt)
end
return
end
if evt == "PET_STABLE_SHOW" then
MTH_ST_RunScanThrottled("event:" .. tostring(evt), 1)
MTH_ST_RunScanThrottled(evt, 1)
return
end
if evt == "PET_STABLE_UPDATE" then
MTH_ST_Scan("event:PET_STABLE_UPDATE")
MTH_ST_Scan(evt)
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_STABLE_UPDATE")
MTH_PETS_EmitLiveState(evt)
return
end
if evt == "PET_STABLE_CLOSED" then
MTH_PETS_RefreshCurrentPet()
MTH_PETS_EmitLiveState("event:PET_STABLE_CLOSED")
MTH_PETS_EmitLiveState(evt)
return
end
end
@@ -2912,6 +2948,37 @@ function MTH_ST_InitService()
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("PET_STABLE_SHOW")
frame:SetScript("OnEvent", function(self, evt)
evt = evt or event
if evt ~= "PET_STABLE_SHOW" then
return
end
MTH_ST_InitService()
if type(MTH_ST_Scan) == "function" then
MTH_ST_Scan("bootstrap:PET_STABLE_SHOW")
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)
if not MTH_StableFrame then
return
+51 -6
View File
@@ -129,6 +129,49 @@ local function MTH_CommandOptions()
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 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"])
@@ -245,6 +288,8 @@ function SlashCmdList.MTH(msg, editbox)
MTH_CommandOptions()
elseif lowerMsg == "book" or lowerMsg == "hunterbook" then
MTH_CommandBook()
elseif lowerMsg == "peers" or lowerMsg == "who" then
MTH_CommandPeers()
elseif lowerMsg == "err" then
if MTH_DebugFrame and type(MTH_DebugFrame.Toggle) == "function" then
MTH_DebugFrame:Toggle()
@@ -259,13 +304,13 @@ end
-- Initialize on load
MTH:InitSavedVariables()
if type(MTH_ST_InitService) == "function" then
MTH_ST_InitService()
if type(MTH_ST_InitBootstrap) == "function" then
MTH_ST_InitBootstrap()
end
if type(MTH_TR_InitService) == "function" then
MTH_TR_InitService()
elseif type(MTH_PT_InitService) == "function" then
MTH_PT_InitService()
if type(MTH_TR_InitBootstrap) == "function" then
MTH_TR_InitBootstrap()
elseif type(MTH_PT_InitBootstrap) == "function" then
MTH_PT_InitBootstrap()
end
if type(MTH_PS_InitService) == "function" then
MTH_PS_InitService()
+93 -41
View File
@@ -2,7 +2,7 @@ MTH_Map = {
enabled = true,
showWorld = true,
showMinimap = true,
activeSource = "beasts",
activeSource = "focus",
providers = {},
nodesByZone = {},
worldPins = {},
@@ -21,6 +21,10 @@ MTH_Map = {
lastWorldReason = "n/a",
lastMiniReason = "n/a",
nodeRevision = 0,
minimapForceRefreshInterval = 1.0,
verboseMiniReasons = false,
minimapTickActive = 0.20,
minimapTickIdle = 0.75,
}
local MTH_MAP_MINIMAP_ZOOM = {
@@ -468,7 +472,18 @@ local function MTH_Map_ClearPendingZoneOpen()
MTH_Map.pendingZoneOpenNextAttemptAt = 0
end
function MTH_Map:EnsureInitialized()
if self.controller then
return true
end
self:Init()
return self.controller ~= nil
end
function MTH_Map:SetSource(key)
if not self:EnsureInitialized() then
return false
end
if not self.providers[key] then
return false
end
@@ -672,6 +687,9 @@ function MTH_Map:SetEnabled(enabled)
if not self.enabled then
self:HideAllPins()
else
if not self:EnsureInitialized() then
return
end
self:UpdateWorldMap()
self:UpdateMinimap()
end
@@ -682,6 +700,9 @@ function MTH_Map:SetWorldEnabled(enabled)
if not self.showWorld then
self:HideWorldPins()
else
if not self:EnsureInitialized() then
return
end
self:UpdateWorldMap()
end
end
@@ -691,6 +712,9 @@ function MTH_Map:SetMinimapEnabled(enabled)
if not self.showMinimap then
self:HideMinimapPins()
else
if not self:EnsureInitialized() then
return
end
self:UpdateMinimap()
end
end
@@ -952,22 +976,21 @@ function MTH_Map:UpdateMinimap()
end
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._lastMiniState = nil
self:HideMinimapPins()
return
end
local minimapSizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"])
local mapSize = minimapSizes and minimapSizes[mapId]
if not mapSize then
self.lastMiniReason = "missing-minimap-size"
if table.getn(nodes) <= 0 then
self.lastMiniReason = "no-nodes-in-zone"
self._lastMiniState = nil
self:HideMinimapPins()
return
end
local now = GetTime() or 0
local xPlayer, yPlayer = GetPlayerMapPosition("player")
if not xPlayer or not yPlayer or (xPlayer == 0 and yPlayer == 0) then
self.lastMiniReason = "player-position-unavailable"
@@ -980,10 +1003,33 @@ function MTH_Map:UpdateMinimap()
yPlayer = yPlayer * 100
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 zoomSet = MTH_MAP_MINIMAP_ZOOM[indoor]
if not zoomSet then
self.lastMiniReason = "missing-zoomset"
self.lastMiniReason = self.verboseMiniReasons and "missing-zoomset" or "missing-zoomset"
self._lastMiniState = nil
self:HideMinimapPins()
return
@@ -991,7 +1037,7 @@ function MTH_Map:UpdateMinimap()
local mapZoom = zoomSet[zoom]
if not mapZoom then
self.lastMiniReason = "missing-mapzoom"
self.lastMiniReason = self.verboseMiniReasons and "missing-mapzoom" or "missing-zoom"
self._lastMiniState = nil
self:HideMinimapPins()
return
@@ -1000,33 +1046,16 @@ function MTH_Map:UpdateMinimap()
local mapWidth = mapSize[1]
local mapHeight = mapSize[2]
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:HideMinimapPins()
return
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 yScale = mapZoom / mapHeight
local xDraw = Minimap:GetWidth() / xScale / 100
local yDraw = Minimap:GetHeight() / yScale / 100
local nodes = self.nodesByZone[mapId]
local shown = 0
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
end
self._lastMiniState = {
mapId = mapId,
zoom = zoom,
indoor = indoor,
xPlayer = xPlayer,
yPlayer = yPlayer,
nodeRevision = self.nodeRevision or 0,
}
local lastState = self._lastMiniState or {}
lastState.mapId = mapId
lastState.zoom = zoom
lastState.indoor = indoor
lastState.xPlayer = xPlayer
lastState.yPlayer = yPlayer
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
function MTH_Map:RegisterDefaultProviders()
@@ -1202,9 +1236,12 @@ function MTH_Map:RegisterDefaultProviders()
end
function MTH_Map:Init()
if self.controller then
return
end
self:BuildZoneLookup()
self:RegisterDefaultProviders()
self:RebuildNodes()
self.nodesByZone = self.nodesByZone or {}
if not self.controller then
self.controller = CreateFrame("Frame", "MTHMapController", UIParent)
@@ -1230,7 +1267,6 @@ function MTH_Map:Init()
if not frame then return end
local now = GetTime() or 0
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.pendingZoneOpenNextAttemptAt or 0) <= now then
@@ -1259,12 +1295,28 @@ function MTH_Map:Init()
end
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
MTH_Map_Log("Map system initialized")
end
MTH_Map:Init()
+5 -1
View File
@@ -169,10 +169,14 @@ end
local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader")
loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function(_, eventName, addonName)
loader:SetScript("OnEvent", function(self, eventName, addonName)
eventName = eventName or event
addonName = addonName or arg1
if eventName == "ADDON_LOADED" and addonName == "MetaHunt" then
MTH_MinimapButton:Initialize()
if self and self.UnregisterAllEvents then
self:UnregisterAllEvents()
self:SetScript("OnEvent", nil)
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)
if not check then return end
MTH_AB_STATE.syncing = true
@@ -60,8 +62,7 @@ local function MTH_AB_GetStore()
if engine and engine.EnsureDefaults then
return engine:EnsureDefaults()
end
MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {}
return MTH_AutoBuy_Saved
return MTH_AB_TRANSIENT_STORE
end
local function MTH_AB_NormalizeRule(rule)
+11 -2
View File
@@ -98,11 +98,20 @@ local function MTH_CHRON_CopyTable(source)
end
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)
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
store.profile = {}
end
+17 -1
View File
@@ -1114,10 +1114,26 @@ function MTH_SetupGeneralOptions()
local tooltipsSection = ensureSection("MetaHuntGeneralTooltipsBox", "Tooltips", topY, 356, "right")
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
tooltipsStore = {}
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
tooltipsStore.beastTooltips = true
end
+163 -5
View File
@@ -8,6 +8,8 @@ VC.nextPublishAt = nil
VC.joinAt = nil
VC.notified = false
VC._invalidVersionWarned = false
VC.maxPersistedPeers = 200
VC.seenPeers = VC.seenPeers or {}
local function VC_GetTimeNow()
if type(time) == "function" then
@@ -63,6 +65,158 @@ local function VC_IsOurChannel(name)
return type(name) == "string" and string.lower(name) == string.lower(VC.channelName)
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()
self.nextPublishAt = VC_GetTimeNow() + math.random(10, 20)
end
@@ -96,13 +250,11 @@ function VC:TryPublish()
end
function VC:HandleRemoteMessage(message, author, channelLabel)
if self.notified then
return
end
if not message or message == "" then
return
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
end
local channelName = string.gsub(tostring(channelLabel or ""), "^%d+%.%s*", "")
@@ -121,6 +273,12 @@ function VC:HandleRemoteMessage(message, author, channelLabel)
if not remoteNumber or remoteNumber <= 0 then
return
end
self:TrackPeer(normalizedAuthor or author, remoteNumber)
if self.notified then
return
end
local localVersionNumber = self:GetLocalVersionNumber()
if not localVersionNumber or localVersionNumber <= 0 then
return
@@ -136,7 +294,6 @@ function VC:HandleRemoteMessage(message, author, channelLabel)
end
if VC.frame then
VC.frame:UnregisterEvent("CHAT_MSG_CHANNEL")
VC.frame:SetScript("OnUpdate", nil)
end
end
@@ -149,6 +306,7 @@ if not VC.frame then
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then
VC:LoadPersistedPeers()
VC.joinAt = GetTime() + 6
VC:ResetPublishDelay()
elseif event == "CHAT_MSG_CHANNEL" then