diff --git a/HailMaryGuildTools.lua b/HailMaryGuildTools.lua index 3e0e2d7..bbef89a 100644 --- a/HailMaryGuildTools.lua +++ b/HailMaryGuildTools.lua @@ -558,6 +558,30 @@ function HMGT:SendOutdatedVersionWhisper(playerName, remoteVersion) return true end +function HMGT:RegisterLibSpecializationBridge() + if self._libSpecializationBridgeRegistered then + return true + end + if not LibStub then + return false + end + + local LibSpec = LibStub("LibSpecialization", true) + if not LibSpec or type(LibSpec.RegisterGroup) ~= "function" then + return false + end + + LibSpec.RegisterGroup(self, function(specId, role, position, playerName, talentString) + if not playerName or not specId then + return + end + local classToken = HMGT:GetClassTokenForSpecId(specId) + HMGT:ApplyExternalSpecInfo("LibSpecialization", playerName, classToken, specId, talentString) + end) + self._libSpecializationBridgeRegistered = true + return true +end + function HMGT:SendReliableAck(target, messageId) if not target or target == "" or not messageId or messageId == "" then return @@ -1814,6 +1838,7 @@ function HMGT:OnEnable() self:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED","OnPlayerTalentUpdate") -- Gruppen-Sichtbarkeit neu auswerten wenn sich die Zusammensetzung ändert self:RegisterEvent("RAID_ROSTER_UPDATE", "OnGroupRosterUpdate") + self:RegisterLibSpecializationBridge() if not self.cleanupTicker then self.cleanupTicker = C_Timer.NewTicker(15, function() self:CleanupStaleCooldowns() end) end @@ -3074,17 +3099,54 @@ function HMGT:RegisterExternalAddonSource(sourceName) return true end +function HMGT:GetCanonicalExternalSpellEntry(spellId) + local sid = tonumber(spellId) + if not sid or sid <= 0 or not HMGT_SpellData then + return nil, sid + end + + local spellEntry = HMGT_SpellData.InterruptLookup and HMGT_SpellData.InterruptLookup[sid] + or HMGT_SpellData.CooldownLookup and HMGT_SpellData.CooldownLookup[sid] + if not spellEntry then + return nil, sid + end + + return spellEntry, tonumber(spellEntry.spellId) or sid +end + +function HMGT:InferClassFromSpellEntry(spellEntry) + if type(spellEntry) ~= "table" or type(spellEntry.classes) ~= "table" then + return nil + end + + local foundClass + for key, value in pairs(spellEntry.classes) do + local classToken = type(value) == "string" and value or key + if foundClass and foundClass ~= classToken then + return nil + end + foundClass = classToken + end + return foundClass +end + function HMGT:ApplyExternalKnownSpell(sourceName, playerName, spellId, class, cooldown) local source = tostring(sourceName or "External") local normalizedName = self:NormalizePlayerName(playerName) local sid = tonumber(spellId) if not normalizedName or normalizedName == "" or not sid or sid <= 0 then - return false + return false, "invalid_args" end if not self:IsPlayerInCurrentGroup(normalizedName) then - return false + return false, "not_in_group" end + local spellEntry, canonicalSid = self:GetCanonicalExternalSpellEntry(sid) + if not spellEntry or not canonicalSid or canonicalSid <= 0 then + return false, "unknown_spell" + end + sid = canonicalSid + self:RegisterExternalAddonSource(source) local previous = self.playerData[normalizedName] or {} local knownSpells = previous.knownSpells @@ -3093,8 +3155,10 @@ function HMGT:ApplyExternalKnownSpell(sourceName, playerName, spellId, class, co end knownSpells[sid] = true + local classToken = class or previous.class or self:InferClassFromSpellEntry(spellEntry) + self.playerData[normalizedName] = { - class = class or previous.class, + class = classToken, specIndex = previous.specIndex, talentHash = previous.talentHash, talents = previous.talents or {}, @@ -3102,14 +3166,93 @@ function HMGT:ApplyExternalKnownSpell(sourceName, playerName, spellId, class, co externalSource = source, } - if tonumber(cooldown) and tonumber(cooldown) > 0 and HMGT_SpellData then - local spellEntry = HMGT_SpellData.InterruptLookup and HMGT_SpellData.InterruptLookup[sid] - or HMGT_SpellData.CooldownLookup and HMGT_SpellData.CooldownLookup[sid] - if spellEntry then - spellEntry._hmgtExternalBaseCd = tonumber(cooldown) + if tonumber(cooldown) and tonumber(cooldown) > 0 then + spellEntry._hmgtExternalBaseCd = tonumber(cooldown) + end + + self:TriggerTrackerUpdate("trackers") + return true +end + +function HMGT:GetClassTokenForSpecId(specId) + local sid = tonumber(specId) + if not sid or sid <= 0 then + return nil + end + + if type(GetSpecializationInfoByID) == "function" then + local returns = { pcall(GetSpecializationInfoByID, sid) } + local ok = returns[1] + local classToken = returns[7] + if ok and type(classToken) == "string" and classToken ~= "" then + return classToken end end + if type(GetSpecializationInfoForClassID) ~= "function" then + return nil + end + + for classID = 1, 20 do + local _, token = GetClassInfo(classID) + if token then + local count = 4 + if type(GetNumSpecializationsForClassID) == "function" then + count = tonumber(GetNumSpecializationsForClassID(classID)) or 4 + end + for index = 1, math.max(1, count) do + local foundSpecId = GetSpecializationInfoForClassID(classID, index) + if tonumber(foundSpecId) == sid then + return token + end + end + end + end + + return nil +end + +function HMGT:ApplyExternalSpecInfo(sourceName, playerName, class, specId, talentHash) + local source = tostring(sourceName or "External") + local normalizedName = self:NormalizePlayerName(playerName) + local spec = tonumber(specId) + local classToken = class and tostring(class) or self:GetClassTokenForSpecId(spec) + if not normalizedName or normalizedName == "" or not classToken or classToken == "" or not spec or spec <= 0 then + return false, "invalid_args" + end + if not self:IsPlayerInCurrentGroup(normalizedName) then + return false, "not_in_group" + end + + self:RegisterExternalAddonSource(source) + local previous = self.playerData[normalizedName] or {} + local knownSpells = previous.knownSpells + if type(knownSpells) ~= "table" then + knownSpells = {} + end + + if HMGT_SpellData and type(HMGT_SpellData.GetSpellsForSpec) == "function" then + for _, datasetName in ipairs({ "Interrupts", "RaidCooldowns", "GroupCooldowns" }) do + local dataset = HMGT_SpellData[datasetName] + for _, spellEntry in ipairs(HMGT_SpellData.GetSpellsForSpec(classToken, spec, dataset)) do + local sid = tonumber(spellEntry and spellEntry.spellId) + if sid and sid > 0 then + knownSpells[sid] = true + end + end + end + end + + self.playerData[normalizedName] = { + class = classToken, + specIndex = spec, + talentHash = talentHash or previous.talentHash, + talents = self:ParseTalentHash(talentHash or previous.talentHash), + knownSpells = knownSpells, + externalSource = source, + } + + self:PruneAvailabilityStates(normalizedName, knownSpells) self:TriggerTrackerUpdate("trackers") return true end @@ -3120,12 +3263,18 @@ function HMGT:ApplyExternalCooldown(sourceName, playerName, spellId, cooldown) local sid = tonumber(spellId) local cd = tonumber(cooldown) if not normalizedName or normalizedName == "" or not sid or sid <= 0 or not cd or cd <= 0 then - return false + return false, "invalid_args" end if not self:IsPlayerInCurrentGroup(normalizedName) then - return false + return false, "not_in_group" end + local spellEntry, canonicalSid = self:GetCanonicalExternalSpellEntry(sid) + if not spellEntry or not canonicalSid or canonicalSid <= 0 then + return false, "unknown_spell" + end + sid = canonicalSid + self:RegisterExternalAddonSource(source) self:ApplyExternalKnownSpell(source, normalizedName, sid, nil, cd) self:HandleRemoteSpellCast(normalizedName, sid, GetServerTime(), nil, nil, nil, cd) @@ -3182,6 +3331,7 @@ function HMGT:ApplyRemoteSpellState(playerName, spellId, kind, revision, a, b, c if not spellEntry then return false end + sid = tonumber(spellEntry.spellId) or sid local now = GetTime() local stateKind = tostring(kind or "") @@ -3639,6 +3789,7 @@ function HMGT:HandleOwnSpellCast(spellId) local spellEntry = HMGT_SpellData.InterruptLookup[spellId] or HMGT_SpellData.CooldownLookup[spellId] + spellId = tonumber(spellEntry and spellEntry.spellId) or spellId local name = self:NormalizePlayerName(UnitName("player")) local pData = self.playerData[name] local talents = pData and pData.talents or {} @@ -3830,6 +3981,7 @@ function HMGT:RefreshOwnCooldownStateFromGame(spellId) if not spellEntry or self:IsAvailabilitySpell(spellEntry) then return false end + sid = tonumber(spellEntry.spellId) or sid local existing = self.activeCDs[ownName] and self.activeCDs[ownName][sid] local before = BuildCooldownStateFingerprint(existing) @@ -4101,6 +4253,7 @@ function HMGT:DidOwnInterruptSucceed(triggerSpellId, talents) local spellEntry = HMGT_SpellData and HMGT_SpellData.InterruptLookup and HMGT_SpellData.InterruptLookup[sid] if not spellEntry then return false end + sid = tonumber(spellEntry.spellId) or sid local _, observedDuration = GetSpellCooldownInfo(sid) observedDuration = tonumber(observedDuration) or 0 @@ -4407,6 +4560,7 @@ function HMGT:HandleRemoteSpellCast(playerName, spellId, castTimestamp, curCharg local spellEntry = HMGT_SpellData.InterruptLookup[spellId] or HMGT_SpellData.CooldownLookup[spellId] if not spellEntry then return end + spellId = tonumber(spellEntry.spellId) or spellId if self:IsAvailabilitySpell(spellEntry) then return end local pData = self.playerData[playerName] @@ -5016,6 +5170,14 @@ function HMGT:SlashCommand(input) else self:Print(L["VERSION_WINDOW_DEVTOOLS_ONLY"] or "HMGT: /hmgt version is only available while developer tools are enabled.") end + elseif input == "bridge" then + if _G.HMGT_Bridge and _G.HMGT_Bridge.GetStatusLines then + for _, line in ipairs(_G.HMGT_Bridge:GetStatusLines()) do + self:Print(line) + end + else + self:Print("HMGT Bridge is not loaded.") + end elseif input == "debug" then if self.ToggleDevToolsWindow then self:ToggleDevToolsWindow() diff --git a/HailMaryGuildTools.toc b/HailMaryGuildTools.toc index 9fa5a27..abc4837 100644 --- a/HailMaryGuildTools.toc +++ b/HailMaryGuildTools.toc @@ -1,4 +1,4 @@ -## Interface: 120000,120001 +## Interface: 120000,120001,120005 ## IconTexture: Interface\Addons\HailMaryGuildTools\Media\HailMaryIcon.png ## Author: Torsten Brendgen ## Title: Hail Mary Guild Tools diff --git a/Modules/Tracker/InterruptTracker/InterruptSpellDatabase.lua b/Modules/Tracker/InterruptTracker/InterruptSpellDatabase.lua index 644a3f4..79aba2b 100644 --- a/Modules/Tracker/InterruptTracker/InterruptSpellDatabase.lua +++ b/Modules/Tracker/InterruptTracker/InterruptSpellDatabase.lua @@ -97,14 +97,23 @@ HMGT_SpellData.Interrupts = { -- WARLOCK Spell(19647, "Spell Lock", { classes = {"WARLOCK"}, - specs = {2}, + specs = {1, 3}, category = "interrupt", state = { kind = "cooldown", cooldown = 24 }, }), + Spell(119914, "Axe Toss", { + classes = {"WARLOCK"}, + specs = {2}, + category = "interrupt", + aliases = { 89766 }, + petSpellId = 89766, + state = { kind = "cooldown", cooldown = 30 }, + }), Spell(132409, "Spell Lock (Grimoire)", { classes = {"WARLOCK"}, specs = {1, 3}, category = "interrupt", + aliases = { 1276467 }, state = { kind = "cooldown", cooldown = 24 }, }), diff --git a/Modules/Tracker/SpellDatabase.lua b/Modules/Tracker/SpellDatabase.lua index 28ffde6..33e8100 100644 --- a/Modules/Tracker/SpellDatabase.lua +++ b/Modules/Tracker/SpellDatabase.lua @@ -1077,6 +1077,18 @@ function HMGT_SpellData.RebuildLookups() for _, entry in ipairs(HMGT_SpellData.Interrupts or {}) do entry._hmgtDataset = "Interrupts" HMGT_SpellData.InterruptLookup[entry.spellId] = entry + if type(entry.aliases) == "table" then + for _, aliasId in ipairs(entry.aliases) do + local sid = tonumber(aliasId) + if sid and sid > 0 then + HMGT_SpellData.InterruptLookup[sid] = entry + end + end + end + local petSpellId = tonumber(entry.petSpellId) + if petSpellId and petSpellId > 0 then + HMGT_SpellData.InterruptLookup[petSpellId] = entry + end end HMGT_SpellData.CooldownLookup = {}