Compare commits

..

3 Commits

Author SHA1 Message Date
Torsten Brendgen
258cadeba5 Dev Build 2.0.1 2026-04-22 16:20:47 +02:00
Torsten Brendgen
8c37da2d38 Adding Events for Hail Mary Bridge Addon 2026-04-21 18:26:12 +02:00
Torsten Brendgen
6151b434b1 Enhance version management features and localization for HMGT 2026-04-16 16:34:01 +02:00
7 changed files with 627 additions and 46 deletions

View File

@@ -3,6 +3,212 @@ local HMGT = _G[ADDON_NAME]
if not HMGT then return end
local L = HMGT.L or LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
local AceGUI = LibStub("AceGUI-3.0", true)
local CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or {}
local function NormalizeName(name)
if HMGT.NormalizePlayerName then
return HMGT:NormalizePlayerName(name)
end
return tostring(name or "")
end
local function GetRosterRows()
local rows = {}
local seen = {}
local function addUnit(unitId)
if not unitId or not UnitExists(unitId) then
return
end
local name = NormalizeName(UnitName(unitId))
if not name or name == "" or seen[name] then
return
end
seen[name] = true
rows[#rows + 1] = {
name = name,
class = select(2, UnitClass(unitId)),
isLeader = UnitIsGroupLeader and UnitIsGroupLeader(unitId) or false,
isAssistant = UnitIsGroupAssistant and UnitIsGroupAssistant(unitId) or false,
connected = UnitIsConnected and UnitIsConnected(unitId) ~= false or true,
isPlayer = UnitIsUnit and UnitIsUnit(unitId, "player") or unitId == "player",
}
end
if IsInRaid() then
for i = 1, GetNumGroupMembers() do
addUnit("raid" .. i)
end
elseif IsInGroup() then
addUnit("player")
for i = 1, GetNumSubgroupMembers() do
addUnit("party" .. i)
end
else
addUnit("player")
end
table.sort(rows, function(a, b)
if a.isLeader ~= b.isLeader then
return a.isLeader
end
if a.isPlayer ~= b.isPlayer then
return a.isPlayer
end
return tostring(a.name or "") < tostring(b.name or "")
end)
return rows
end
local function GetPlayerVersionText(name)
local normalized = NormalizeName(name)
if normalized == NormalizeName(UnitName("player")) then
return tostring(HMGT.ADDON_VERSION or "dev"), tonumber(HMGT.PROTOCOL_VERSION) or 0, true
end
local version = HMGT.peerVersions and HMGT.peerVersions[normalized] or nil
local protocol = HMGT.GetPeerProtocolVersion and HMGT:GetPeerProtocolVersion(normalized) or 0
if version and version ~= "" then
return tostring(version), tonumber(protocol) or 0, true
end
return nil, tonumber(protocol) or 0, false
end
local function ApplyClassIcon(texture, classTag)
if not texture then
return
end
local coords = classTag and CLASS_ICON_TCOORDS[classTag]
if coords then
texture:SetTexture("Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES")
texture:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
texture:Show()
else
texture:SetTexture(nil)
texture:Hide()
end
end
local function AcquireVersionRow(window, index)
window.versionRows = window.versionRows or {}
local row = window.versionRows[index]
if row then
return row
end
local parent = window.scrollChild
row = CreateFrame("Frame", nil, parent)
row:SetHeight(22)
row.background = row:CreateTexture(nil, "BACKGROUND")
row.background:SetAllPoints(row)
row.background:SetColorTexture(1, 1, 1, 0.03)
row.classIcon = row:CreateTexture(nil, "ARTWORK")
row.classIcon:SetSize(16, 16)
row.classIcon:SetPoint("LEFT", row, "LEFT", 4, 0)
row.nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
row.nameText:SetPoint("LEFT", row.classIcon, "RIGHT", 6, 0)
row.nameText:SetJustifyH("LEFT")
row.versionText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
row.versionText:SetPoint("LEFT", row, "LEFT", 250, 0)
row.versionText:SetWidth(150)
row.versionText:SetJustifyH("LEFT")
row.protocolText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
row.protocolText:SetPoint("LEFT", row, "LEFT", 410, 0)
row.protocolText:SetWidth(100)
row.protocolText:SetJustifyH("LEFT")
window.versionRows[index] = row
return row
end
function HMGT:RefreshVersionNoticeWindow()
local window = self.versionNoticeWindow
if not window then
return
end
local roster = GetRosterRows()
local localName = NormalizeName(UnitName("player"))
for index, info in ipairs(roster) do
local row = AcquireVersionRow(window, index)
row:ClearAllPoints()
if index == 1 then
row:SetPoint("TOPLEFT", window.scrollChild, "TOPLEFT", 0, 0)
row:SetPoint("TOPRIGHT", window.scrollChild, "TOPRIGHT", 0, 0)
else
row:SetPoint("TOPLEFT", window.versionRows[index - 1], "BOTTOMLEFT", 0, -2)
row:SetPoint("TOPRIGHT", window.versionRows[index - 1], "BOTTOMRIGHT", 0, -2)
end
ApplyClassIcon(row.classIcon, info.class)
local nameLabel = tostring(info.name or UNKNOWN)
if info.isLeader then
nameLabel = string.format("%s %s", nameLabel, L["VERSION_WINDOW_LEADER_TAG"] or "(Leader)")
elseif info.isAssistant then
nameLabel = string.format("%s %s", nameLabel, L["VERSION_WINDOW_ASSISTANT_TAG"] or "(Assist)")
end
if info.isPlayer or info.name == localName then
nameLabel = string.format("%s %s", nameLabel, L["VERSION_WINDOW_SELF_TAG"] or "(You)")
end
row.nameText:SetText(nameLabel)
row.nameText:SetTextColor(1, 0.82, 0.1, 1)
local versionText, protocol, hasAddon = GetPlayerVersionText(info.name)
if hasAddon then
row.versionText:SetText(versionText or "?")
row.versionText:SetTextColor(0.9, 0.9, 0.9, 1)
row.protocolText:SetText(protocol > 0 and tostring(protocol) or "-")
row.protocolText:SetTextColor(0.75, 0.75, 0.75, 1)
else
row.versionText:SetText(L["VERSION_WINDOW_MISSING_ADDON"] or "Addon not installed")
row.versionText:SetTextColor(1, 0.25, 0.25, 1)
row.protocolText:SetText("-")
row.protocolText:SetTextColor(1, 0.25, 0.25, 1)
end
row:Show()
end
if window.versionRows then
for index = #roster + 1, #window.versionRows do
window.versionRows[index]:Hide()
end
end
local contentHeight = math.max(1, (#roster * 24))
window.scrollChild:SetHeight(contentHeight)
local known = 0
for _, info in ipairs(roster) do
local _, _, hasAddon = GetPlayerVersionText(info.name)
if hasAddon then
known = known + 1
end
end
window.messageText:SetText(L["VERSION_WINDOW_MESSAGE"] or "Hail Mary Guild Tools versions in your current group")
window.detailText:SetText(string.format(
L["VERSION_WINDOW_CURRENT"] or "Current version: %s | Protocol: %s",
tostring(HMGT.ADDON_VERSION or "dev"),
tostring(HMGT.PROTOCOL_VERSION or "?")
))
window:SetStatusText(string.format(
L["VERSION_WINDOW_STATUS"] or "Detected HMGT on %d/%d players",
tonumber(known) or 0,
tonumber(#roster) or 0
))
end
function HMGT:EnsureVersionNoticeWindow()
if self.versionNoticeWindow then
@@ -10,21 +216,21 @@ function HMGT:EnsureVersionNoticeWindow()
end
self.versionNoticeWindowStatus = self.versionNoticeWindowStatus or {
width = 560,
height = 240,
width = 640,
height = 420,
}
local window = self:CreateAceWindow("versionNotice", {
title = L["VERSION_WINDOW_TITLE"] or "HMGT Version Check",
statusText = "",
statusTable = self.versionNoticeWindowStatus,
width = self.versionNoticeWindowStatus.width or 560,
height = self.versionNoticeWindowStatus.height or 240,
width = self.versionNoticeWindowStatus.width or 640,
height = self.versionNoticeWindowStatus.height or 420,
backgroundTexture = "Interface\\AddOns\\HailMaryGuildTools\\Media\\HailMaryLogo.png",
backgroundWidth = 220,
backgroundHeight = 120,
backgroundOffsetY = -8,
backgroundAlpha = 0.12,
backgroundAlpha = 0.08,
strata = "FULLSCREEN_DIALOG",
})
if not window then
@@ -32,26 +238,64 @@ function HMGT:EnsureVersionNoticeWindow()
end
local content = window:GetContent()
local messageText = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
messageText:SetPoint("TOPLEFT", content, "TOPLEFT", 28, -28)
messageText:SetPoint("TOPRIGHT", content, "TOPRIGHT", -28, -28)
messageText:SetJustifyH("CENTER")
messageText:SetJustifyV("MIDDLE")
messageText:SetPoint("TOPLEFT", content, "TOPLEFT", 24, -22)
messageText:SetPoint("TOPRIGHT", content, "TOPRIGHT", -24, -22)
messageText:SetJustifyH("LEFT")
messageText:SetTextColor(1, 0.82, 0.1, 1)
messageText:SetText(L["VERSION_WINDOW_MESSAGE"] or "A new version of Hail Mary Guild Tools is available.")
window.messageText = messageText
local detailText = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
detailText:SetPoint("TOPLEFT", messageText, "BOTTOMLEFT", 0, -18)
detailText:SetPoint("TOPRIGHT", messageText, "BOTTOMRIGHT", 0, -18)
detailText:SetJustifyH("CENTER")
detailText:SetJustifyV("TOP")
if detailText.SetSpacing then
detailText:SetSpacing(2)
end
detailText:SetPoint("TOPLEFT", messageText, "BOTTOMLEFT", 0, -8)
detailText:SetPoint("TOPRIGHT", messageText, "BOTTOMRIGHT", 0, -8)
detailText:SetJustifyH("LEFT")
detailText:SetTextColor(0.9, 0.9, 0.9, 1)
window.detailText = detailText
local refreshButton = AceGUI and AceGUI:Create("Button") or nil
if refreshButton then
refreshButton:SetText(L["VERSION_WINDOW_REFRESH"] or "Refresh")
refreshButton:SetWidth(120)
refreshButton:SetCallback("OnClick", function()
HMGT:RequestSync("VersionWindow")
HMGT:RefreshVersionNoticeWindow()
end)
refreshButton.frame:SetParent(window.frame)
refreshButton.frame:ClearAllPoints()
refreshButton.frame:SetPoint("TOPRIGHT", content, "TOPRIGHT", -24, -68)
refreshButton.frame:Show()
window.refreshButton = refreshButton
end
local header = CreateFrame("Frame", nil, content)
header:SetPoint("TOPLEFT", detailText, "BOTTOMLEFT", 0, -14)
header:SetPoint("TOPRIGHT", content, "TOPRIGHT", -24, -96)
header:SetHeight(18)
window.header = header
local nameHeader = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
nameHeader:SetPoint("LEFT", header, "LEFT", 24, 0)
nameHeader:SetText(L["VERSION_WINDOW_COLUMN_PLAYER"] or "Player")
local versionHeader = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
versionHeader:SetPoint("LEFT", header, "LEFT", 250, 0)
versionHeader:SetText(L["VERSION_WINDOW_COLUMN_VERSION"] or "Version")
local protocolHeader = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
protocolHeader:SetPoint("LEFT", header, "LEFT", 410, 0)
protocolHeader:SetText(L["VERSION_WINDOW_COLUMN_PROTOCOL"] or "Protocol")
local scrollFrame = CreateFrame("ScrollFrame", nil, content, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -6)
scrollFrame:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", -28, 24)
window.scrollFrame = scrollFrame
local scrollChild = CreateFrame("Frame", nil, scrollFrame)
scrollChild:SetSize(1, 1)
scrollFrame:SetScrollChild(scrollChild)
window.scrollChild = scrollChild
self.versionNoticeWindow = window
return window
end
@@ -67,36 +311,16 @@ function HMGT:ShowVersionMismatchPopup(playerName, detail, sourceTag, opts)
}
end
local info = self.latestVersionMismatch or {}
local window = self:EnsureVersionNoticeWindow()
if not window then
return
end
local hasMismatch = info.playerName or info.detail
window:SetTitle(L["VERSION_WINDOW_TITLE"] or "HMGT Version Check")
if hasMismatch then
window.messageText:SetText(L["VERSION_WINDOW_MESSAGE"] or "A new version of Hail Mary Guild Tools is available.")
window.detailText:SetText(string.format(
L["VERSION_WINDOW_DETAIL"] or "Detected via %s from %s.\n%s",
tostring(info.sourceTag or "?"),
tostring(info.playerName or UNKNOWN),
tostring(info.detail or "")
))
else
window.messageText:SetText(L["VERSION_WINDOW_NO_MISMATCH"] or "No newer HMGT version has been detected in your current group.")
window.detailText:SetText(string.format(
L["VERSION_WINDOW_CURRENT"] or "Current version: %s | Protocol: %s",
tostring(HMGT.ADDON_VERSION or "dev"),
tostring(HMGT.PROTOCOL_VERSION or "?")
))
end
self:DevTrace("Version", hasMismatch and "window_show_mismatch" or "window_show_current", {
player = info.playerName,
source = info.sourceTag,
detail = info.detail,
self:RefreshVersionNoticeWindow()
self:DevTrace("Version", "window_show", {
player = playerName,
source = sourceTag,
detail = detail,
})
window:Show()
window:Raise()

View File

@@ -280,6 +280,7 @@ HMGT.pendingSpellPowerCosts = {}
HMGT.demoModeData = {}
HMGT.peerVersions = {}
HMGT.versionWarnings = {}
HMGT.versionWhisperWarnings = {}
HMGT.debugBuffer = {}
HMGT.debugBufferMax = 500
HMGT.enabledDebugScopes = {
@@ -484,6 +485,103 @@ function HMGT:RememberPeerProtocolVersion(playerName, protocol)
self.peerProtocols[normalizedName] = numeric
end
local function ParseVersionTokens(version)
local tokens = {}
local text = tostring(version or "")
for number in string.gmatch(text, "(%d+)") do
tokens[#tokens + 1] = tonumber(number) or 0
end
return tokens
end
function HMGT:CompareAddonVersions(leftVersion, rightVersion)
local left = ParseVersionTokens(leftVersion)
local right = ParseVersionTokens(rightVersion)
local count = math.max(#left, #right)
for i = 1, count do
local a = tonumber(left[i]) or 0
local b = tonumber(right[i]) or 0
if a ~= b then
return (a < b) and -1 or 1
end
end
local leftText = tostring(leftVersion or "")
local rightText = tostring(rightVersion or "")
if leftText == rightText then
return 0
end
if leftText < rightText then
return -1
end
return 1
end
function HMGT:IsPlayerGroupLeader()
if not IsInGroup() and not IsInRaid() then
return true
end
return UnitIsGroupLeader and UnitIsGroupLeader("player") or false
end
function HMGT:SendOutdatedVersionWhisper(playerName, remoteVersion)
local target = self:NormalizePlayerName(playerName)
local localVersion = tostring(self.ADDON_VERSION or "dev")
local remoteText = tostring(remoteVersion or "?")
if not target or target == "" or not self:IsPlayerGroupLeader() then
return false
end
if self:CompareAddonVersions(localVersion, remoteText) <= 0 then
return false
end
local warningKey = string.format("%s|%s|%s", tostring(target), remoteText, localVersion)
if self.versionWhisperWarnings[warningKey] then
return false
end
self.versionWhisperWarnings[warningKey] = true
local message = string.format(
L["VERSION_OUTDATED_WHISPER"] or "Your Hail Mary Guild Tools version is outdated. You have %s, the group leader has %s.",
remoteText,
localVersion
)
if C_ChatInfo and type(C_ChatInfo.SendChatMessage) == "function" then
C_ChatInfo.SendChatMessage(message, "WHISPER", nil, target)
elseif type(SendChatMessage) == "function" then
SendChatMessage(message, "WHISPER", nil, target)
else
return false
end
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
@@ -682,6 +780,12 @@ function HMGT:RegisterPeerVersion(playerName, version, protocol, sourceTag)
if not playerName then return end
self.peerVersions[playerName] = version
self:RememberPeerProtocolVersion(playerName, protocol)
if self.versionNoticeWindow and self.versionNoticeWindow.IsShown and self.versionNoticeWindow:IsShown() and self.RefreshVersionNoticeWindow then
self:RefreshVersionNoticeWindow()
end
if version and version ~= "" then
self:SendOutdatedVersionWhisper(playerName, version)
end
local mismatch = false
local details = {}
if version and version ~= "" and version ~= ADDON_VERSION then
@@ -1734,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
@@ -2984,6 +3089,198 @@ function HMGT:StoreRemotePlayerInfo(playerName, class, specIndex, talentHash, kn
)
end
function HMGT:RegisterExternalAddonSource(sourceName)
local source = tostring(sourceName or "")
if source == "" then
return false
end
self.externalAddonSources = self.externalAddonSources or {}
self.externalAddonSources[source] = true
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, "invalid_args"
end
if not self:IsPlayerInCurrentGroup(normalizedName) then
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
if type(knownSpells) ~= "table" then
knownSpells = {}
end
knownSpells[sid] = true
local classToken = class or previous.class or self:InferClassFromSpellEntry(spellEntry)
self.playerData[normalizedName] = {
class = classToken,
specIndex = previous.specIndex,
talentHash = previous.talentHash,
talents = previous.talents or {},
knownSpells = knownSpells,
externalSource = source,
}
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
function HMGT:ApplyExternalCooldown(sourceName, playerName, spellId, cooldown)
local source = tostring(sourceName or "External")
local normalizedName = self:NormalizePlayerName(playerName)
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, "invalid_args"
end
if not self:IsPlayerInCurrentGroup(normalizedName) then
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)
return true
end
function HMGT:ClearRemoteSpellState(playerName, spellId)
local normalizedName = self:NormalizePlayerName(playerName)
local sid = tonumber(spellId)
@@ -3034,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 "")
@@ -3491,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 {}
@@ -3682,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)
@@ -3953,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
@@ -4259,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]
@@ -4436,11 +4738,17 @@ function HMGT:OnGroupRosterUpdate()
self.remoteSpellStateRevisions[name] = nil
self.peerVersions[name] = nil
self.versionWarnings[name] = nil
if self.peerProtocols then
self.peerProtocols[name] = nil
end
end
end
local count = 0
for _ in pairs(validPlayers) do count = count + 1 end
self:Debug("verbose", "OnGroupRosterUpdate validPlayers=%d", count)
if self.versionNoticeWindow and self.versionNoticeWindow.IsShown and self.versionNoticeWindow:IsShown() and self.RefreshVersionNoticeWindow then
self:RefreshVersionNoticeWindow()
end
if HMGT.TrackerManager and HMGT.TrackerManager.InvalidateAnchorLayout then
HMGT.TrackerManager:InvalidateAnchorLayout()
end
@@ -4862,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()

View File

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

View File

@@ -18,10 +18,20 @@ L["SLASH_HINT"] = "/hmgt Optionen | /hmgt lock/unlock | /hmgt dem
L["VERSION_MISMATCH_CHAT"] = "Versionskonflikt mit %s: %s"
L["VERSION_MISMATCH_POPUP"] = "HailMaryGuildTools Konflikt mit %s.\n%s\nQuelle: %s"
L["VERSION_WINDOW_TITLE"] = "HMGT Versionscheck"
L["VERSION_WINDOW_MESSAGE"] = "Es gibt eine neue Version von Hail Mary Guild Tools."
L["VERSION_WINDOW_MESSAGE"] = "Hail Mary Guild Tools Versionen in deiner aktuellen Gruppe"
L["VERSION_WINDOW_DETAIL"] = "Erkannt ueber %s von %s.\n%s"
L["VERSION_WINDOW_NO_MISMATCH"] = "In deiner aktuellen Gruppe wurde keine neuere HMGT-Version erkannt."
L["VERSION_WINDOW_CURRENT"] = "Aktuelle Version: %s | Protokoll: %s"
L["VERSION_WINDOW_STATUS"] = "HMGT bei %d/%d Spielern erkannt"
L["VERSION_WINDOW_REFRESH"] = "Aktualisieren"
L["VERSION_WINDOW_COLUMN_PLAYER"] = "Spieler"
L["VERSION_WINDOW_COLUMN_VERSION"] = "Version"
L["VERSION_WINDOW_COLUMN_PROTOCOL"] = "Protokoll"
L["VERSION_WINDOW_MISSING_ADDON"] = "Addon nicht vorhanden"
L["VERSION_WINDOW_LEADER_TAG"] = "(Leiter)"
L["VERSION_WINDOW_ASSISTANT_TAG"] = "(Assist)"
L["VERSION_WINDOW_SELF_TAG"] = "(Du)"
L["VERSION_OUTDATED_WHISPER"] = "Deine Hail Mary Guild Tools Version ist veraltet. Du hast %s, der Gruppenleiter hat %s."
L["VERSION_WINDOW_DEBUG_ONLY"] = "HMGT: /hmgt version ist nur bei aktiviertem Debugmodus verfuegbar."
L["VERSION_WINDOW_DEVTOOLS_ONLY"] = "HMGT: /hmgt version ist nur bei aktivierten Entwicklerwerkzeugen verfuegbar."

View File

@@ -18,10 +18,20 @@ L["SLASH_HINT"] = "/hmgt options | /hmgt lock/unlock | /hmgt demo
L["VERSION_MISMATCH_CHAT"] = "Version mismatch with %s: %s"
L["VERSION_MISMATCH_POPUP"] = "HailMaryGuildTools mismatch with %s.\n%s\nSource: %s"
L["VERSION_WINDOW_TITLE"] = "HMGT Version Check"
L["VERSION_WINDOW_MESSAGE"] = "A new version of Hail Mary Guild Tools is available."
L["VERSION_WINDOW_MESSAGE"] = "Hail Mary Guild Tools versions in your current group"
L["VERSION_WINDOW_DETAIL"] = "Detected via %s from %s.\n%s"
L["VERSION_WINDOW_NO_MISMATCH"] = "No newer HMGT version has been detected in your current group."
L["VERSION_WINDOW_CURRENT"] = "Current version: %s | Protocol: %s"
L["VERSION_WINDOW_STATUS"] = "Detected HMGT on %d/%d players"
L["VERSION_WINDOW_REFRESH"] = "Refresh"
L["VERSION_WINDOW_COLUMN_PLAYER"] = "Player"
L["VERSION_WINDOW_COLUMN_VERSION"] = "Version"
L["VERSION_WINDOW_COLUMN_PROTOCOL"] = "Protocol"
L["VERSION_WINDOW_MISSING_ADDON"] = "Addon not installed"
L["VERSION_WINDOW_LEADER_TAG"] = "(Leader)"
L["VERSION_WINDOW_ASSISTANT_TAG"] = "(Assist)"
L["VERSION_WINDOW_SELF_TAG"] = "(You)"
L["VERSION_OUTDATED_WHISPER"] = "Your Hail Mary Guild Tools version is outdated. You have %s, the group leader has %s."
L["VERSION_WINDOW_DEBUG_ONLY"] = "HMGT: /hmgt version is only available while debug mode is enabled."
L["VERSION_WINDOW_DEVTOOLS_ONLY"] = "HMGT: /hmgt version is only available while developer tools are enabled."

View File

@@ -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 },
}),

View File

@@ -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 = {}