|
|
|
|
@@ -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
|
|
|
|
|
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()
|
|
|
|
|
|