From f97b7556cdebfeff33268e9b46af1eb8ed75e6c9 Mon Sep 17 00:00:00 2001 From: Torsten Brendgen Date: Tue, 28 Apr 2026 23:09:04 +0200 Subject: [PATCH] Refactor code structure for improved readability and maintainability --- HailMaryGuildTools.lua | 101 ++ HailMaryGuildTools.toc | 5 + HailMaryGuildToolsOptions.lua | 29 +- Locales/deDE.lua | 32 +- Locales/enUS.lua | 32 +- Modules/EncounterAlerts/EncounterAlerts.lua | 102 ++ .../EncounterAlertsOptions.lua | 414 ++++++ Modules/EncounterAlerts/LuraRunes.lua | 1113 +++++++++++++++++ .../EncounterAlerts/Media/LuraRunes/.gitkeep | 1 + .../Media/LuraRunes/Rune_Circle.tga | Bin 0 -> 65554 bytes .../Media/LuraRunes/Rune_Diamond.tga | Bin 0 -> 65554 bytes .../Media/LuraRunes/Rune_T.tga | Bin 0 -> 65554 bytes .../Media/LuraRunes/Rune_Triangle.tga | Bin 0 -> 65554 bytes .../Media/LuraRunes/Rune_X.tga | Bin 0 -> 65554 bytes Modules/Tracker/TrackerSync.lua | 5 + readme.md | 16 +- 16 files changed, 1841 insertions(+), 9 deletions(-) create mode 100644 Modules/EncounterAlerts/EncounterAlerts.lua create mode 100644 Modules/EncounterAlerts/EncounterAlertsOptions.lua create mode 100644 Modules/EncounterAlerts/LuraRunes.lua create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/.gitkeep create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/Rune_Circle.tga create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/Rune_Diamond.tga create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/Rune_T.tga create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/Rune_Triangle.tga create mode 100644 Modules/EncounterAlerts/Media/LuraRunes/Rune_X.tga diff --git a/HailMaryGuildTools.lua b/HailMaryGuildTools.lua index e422c33..959d480 100644 --- a/HailMaryGuildTools.lua +++ b/HailMaryGuildTools.lua @@ -40,6 +40,7 @@ local MSG_SYNC_REQUEST = "SRQ" local MSG_SYNC_RESPONSE = "SRS" -- SRS|version|protocol|class|spec|talentHash|knownSpellIds|cd1:t1:d1;... local MSG_RAID_TIMELINE = "RTL" -- RTL|encounterId|time|spellId|leadTime|alertText local MSG_RAID_TIMELINE_TEST = "RTS" -- RTS|encounterId|difficultyId|serverStartTime|duration +local MSG_LURA_RUNES = "LUR" -- LUR|slot1,slot2,slot3,slot4,slot5 local MSG_RELIABLE = "REL" -- REL|messageId|innerPayload local MSG_ACK = "ACK" -- ACK|messageId local COMM_PREFIX = "HMGT" @@ -85,6 +86,7 @@ HMGT.MSG_RELIABLE = MSG_RELIABLE HMGT.MSG_ACK = MSG_ACK HMGT.MSG_RAID_TIMELINE = MSG_RAID_TIMELINE HMGT.MSG_RAID_TIMELINE_TEST = MSG_RAID_TIMELINE_TEST +HMGT.MSG_LURA_RUNES = MSG_LURA_RUNES -- ── Standardwerte ───────────────────────────────────────────── local defaults = { @@ -119,6 +121,34 @@ local defaults = { alertColor = { r = 1, g = 0.82, b = 0.15, a = 1 }, encounters = {}, }, + encounterAlerts = { + enabled = false, + luraRunes = { + enabled = false, + unlocked = false, + posX = 0, + posY = -120, + iconSize = 44, + backgroundAlpha = 0.14, + showLabels = true, + actionBar = { + shown = false, + autoShow = true, + unlocked = false, + posX = 0, + posY = -300, + iconSize = 42, + iconSpacing = 8, + orientation = "horizontal", + border = { + enabled = false, + width = 2, + color = { r = 1, g = 0.82, b = 0.1, a = 0.9 }, + }, + }, + slots = {}, + }, + }, notes = { enabled = true, mainText = "", @@ -196,6 +226,7 @@ local DEBUG_SCOPE_LABELS = { PowerSpend = "Power Spend", RaidTimeline = "Raid Timeline", Notes = "Notes", + EncounterAlerts = "Encounter Alerts", } local DEBUG_LEVELS = { error = 1, @@ -1458,6 +1489,67 @@ local function NormalizeRaidTimelineSettings(settings) settings.encounters = normalizedEncounters end +local VALID_LURA_RUNE_KEYS = { + circle = true, + cross = true, + diamond = true, + t = true, + triangle = true, +} + +local function NormalizeLuraRuneKey(value) + local key = tostring(value or ""):lower() + if VALID_LURA_RUNE_KEYS[key] then + return key + end + return "" +end + +local function NormalizeLuraRunesSettings(settings) + if type(settings) ~= "table" then return end + settings.enabled = settings.enabled == true + settings.unlocked = settings.unlocked == true + settings.posX = math.floor(NormalizeLayoutValue(settings.posX, -1200, 1200, 0) + 0.5) + settings.posY = math.floor(NormalizeLayoutValue(settings.posY, -900, 900, -120) + 0.5) + settings.iconSize = math.floor(NormalizeLayoutValue(settings.iconSize, 28, 80, 44) + 0.5) + settings.backgroundAlpha = NormalizeLayoutValue(settings.backgroundAlpha, 0, 0.8, 0.14) + settings.showLabels = settings.showLabels ~= false + settings.actionBar = type(settings.actionBar) == "table" and settings.actionBar or {} + settings.actionBar.shown = settings.actionBar.shown == true + settings.actionBar.autoShow = settings.actionBar.autoShow ~= false + settings.actionBar.unlocked = settings.actionBar.unlocked == true + settings.actionBar.posX = math.floor(NormalizeLayoutValue(settings.actionBar.posX, -1200, 1200, 0) + 0.5) + settings.actionBar.posY = math.floor(NormalizeLayoutValue(settings.actionBar.posY, -900, 900, -300) + 0.5) + settings.actionBar.iconSize = math.floor(NormalizeLayoutValue(settings.actionBar.iconSize, 28, 80, 42) + 0.5) + settings.actionBar.iconSpacing = math.floor(NormalizeLayoutValue(settings.actionBar.iconSpacing, 0, 80, 8) + 0.5) + settings.actionBar.orientation = tostring(settings.actionBar.orientation or "horizontal") + if settings.actionBar.orientation ~= "vertical" then + settings.actionBar.orientation = "horizontal" + end + settings.actionBar.border = type(settings.actionBar.border) == "table" and settings.actionBar.border or {} + settings.actionBar.border.enabled = settings.actionBar.border.enabled == true + settings.actionBar.border.width = math.floor(NormalizeLayoutValue(settings.actionBar.border.width, 1, 12, 2) + 0.5) + settings.actionBar.border.color = type(settings.actionBar.border.color) == "table" and settings.actionBar.border.color or {} + settings.actionBar.border.color.r = NormalizeLayoutValue(settings.actionBar.border.color.r, 0, 1, 1) + settings.actionBar.border.color.g = NormalizeLayoutValue(settings.actionBar.border.color.g, 0, 1, 0.82) + settings.actionBar.border.color.b = NormalizeLayoutValue(settings.actionBar.border.color.b, 0, 1, 0.1) + settings.actionBar.border.color.a = NormalizeLayoutValue(settings.actionBar.border.color.a, 0, 1, 0.9) + + local slots = type(settings.slots) == "table" and settings.slots or {} + local normalizedSlots = {} + for slot = 1, 5 do + normalizedSlots[slot] = NormalizeLuraRuneKey(slots[slot]) + end + settings.slots = normalizedSlots +end + +local function NormalizeEncounterAlertsSettings(settings) + if type(settings) ~= "table" then return end + settings.enabled = settings.enabled == true + settings.luraRunes = type(settings.luraRunes) == "table" and settings.luraRunes or {} + NormalizeLuraRunesSettings(settings.luraRunes) +end + local function NormalizeNotesSettings(settings) if type(settings) ~= "table" then return end settings.enabled = settings.enabled ~= false @@ -1762,6 +1854,8 @@ function HMGT:MigrateProfileSettings() p.personalAuras = nil p.raidTimeline = p.raidTimeline or {} NormalizeRaidTimelineSettings(p.raidTimeline) + p.encounterAlerts = p.encounterAlerts or {} + NormalizeEncounterAlertsSettings(p.encounterAlerts) p.notes = p.notes or {} NormalizeNotesSettings(p.notes) p.minimap = p.minimap or {} @@ -3029,6 +3123,7 @@ function HMGT:GetHealthStatusLines() AuraExpiry = self.AuraExpiry ~= nil, MapOverlay = self.MapOverlay ~= nil, RaidTimeline = self.RaidTimeline ~= nil, + EncounterAlerts = self.EncounterAlerts ~= nil, Notes = self.Notes ~= nil, } local moduleParts = {} @@ -3108,6 +3203,12 @@ function HMGT:SlashCommand(input) else self:OpenConfig() end + elseif input:find("^lura") == 1 then + if self.EncounterAlerts and self.EncounterAlerts.HandleSlashCommand then + self.EncounterAlerts:HandleSlashCommand(input) + else + self:OpenConfig() + end elseif input:find("^debugdump") == 1 then local n = tonumber(input:match("^debugdump%s+(%d+)$")) if self.DumpDevToolsLog then diff --git a/HailMaryGuildTools.toc b/HailMaryGuildTools.toc index bef1e5e..4da4cc6 100644 --- a/HailMaryGuildTools.toc +++ b/HailMaryGuildTools.toc @@ -66,3 +66,8 @@ Modules\RaidTimeline\RaidTimelineBossAbilityData.lua Modules\RaidTimeline\RaidTimeline.lua Modules\RaidTimeline\RaidTimelineBigWigs.lua Modules\RaidTimeline\RaidTimelineOptions.lua + +# EncounterAlerts +Modules\EncounterAlerts\EncounterAlerts.lua +Modules\EncounterAlerts\LuraRunes.lua +Modules\EncounterAlerts\EncounterAlertsOptions.lua diff --git a/HailMaryGuildToolsOptions.lua b/HailMaryGuildToolsOptions.lua index 948bbef..fec4454 100644 --- a/HailMaryGuildToolsOptions.lua +++ b/HailMaryGuildToolsOptions.lua @@ -17,6 +17,9 @@ function HMGT_Config:RegisterOptionsProvider(id, provider) if type(id) ~= "string" or id == "" then return false end if type(provider) ~= "function" then return false end self._optionProviders[id] = provider + if type(self.RebuildRootOptions) == "function" then + self:RebuildRootOptions() + end return true end @@ -1933,6 +1936,7 @@ function HMGT_Config:Initialize() "|cffffd100/hmgt|r", "|cffffd100/hmgt debug|r", "|cffffd100/hmgt status|r", + "|cffffd100/hmgt lura|r", "|cffffd100/hmgt version|r", }, "\n"), }, @@ -2083,6 +2087,15 @@ function HMGT_Config:Initialize() modulesGroup.args.raidTimeline = raidTimelineGroup end + local encounterAlertsGroup = BuildNamedModuleGroup( + "encounterAlerts", + L["OPT_MODULE_ENCOUNTER_ALERTS"] or "Encounter Alerts", + 50 + ) + if encounterAlertsGroup then + modulesGroup.args.encounterAlerts = encounterAlertsGroup + end + if next(modulesGroup.args) == nil then return nil end @@ -2120,12 +2133,20 @@ function HMGT_Config:Initialize() }, } - local modulesGroup = BuildModulesGroup() - if modulesGroup then - rootOptions.args.modules = modulesGroup + function HMGT_Config:RebuildRootOptions() + local modulesGroup = BuildModulesGroup() + if modulesGroup then + rootOptions.args.modules = modulesGroup + else + rootOptions.args.modules = nil + end + NormalizeExecuteButtonWidths(rootOptions) + if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then + AceConfigRegistry:NotifyChange(ADDON_NAME) + end end - NormalizeExecuteButtonWidths(rootOptions) + HMGT_Config:RebuildRootOptions() local aceConfig = LibStub("AceConfig-3.0") local aceConfigDialog = LibStub("AceConfigDialog-3.0") diff --git a/Locales/deDE.lua b/Locales/deDE.lua index 6a6d0a8..b369276 100644 --- a/Locales/deDE.lua +++ b/Locales/deDE.lua @@ -34,7 +34,7 @@ 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." +L["VERSION_WINDOW_DEVTOOLS_ONLY"] = "HMGT: /hmgt version ist nur bei aktivierter Debug-Konsole verfuegbar." -- ── Options: general ───────────────────────────────────────── L["OPT_GENERAL"] = "Allgemein" @@ -75,6 +75,36 @@ L["OPT_DEVTOOLS_OPEN"] = "Debug-Konsole oeffnen" L["OPT_DEVTOOLS_CLEAR"] = "Debug-Log leeren" L["OPT_DEVTOOLS_SELECT_ALL"] = "Alles markieren" L["OPT_DEVTOOLS_DISABLED"] = "HMGT: Entwicklerwerkzeuge sind nicht aktiviert." +L["OPT_MODULE_ENCOUNTER_ALERTS"] = "Encounter Alerts" +L["OPT_ENCOUNTER_ALERTS_PLACEHOLDER"] = "Encounter-spezifische Helper-Frames und Warnungen." +L["OPT_EA_LURA_TITLE"] = "L'ura Runen" +L["OPT_EA_LURA_RUNE_WINDOW"] = "Runen-Fenster" +L["OPT_EA_LURA_ENABLED"] = "L'ura Runen aktivieren" +L["OPT_EA_LURA_UNLOCK"] = "Runen-Frame entsperren" +L["OPT_EA_LURA_HINT"] = "Erste Version: nur Normal/Heroisch Layout. Tank steht unten mittig zwischen Slot 1 und 5." +L["OPT_EA_LURA_SHOW"] = "Anzeigen" +L["OPT_EA_LURA_TEST"] = "Testmuster" +L["OPT_EA_LURA_CLEAR"] = "Leeren" +L["OPT_EA_LURA_BROADCAST"] = "Sequenz senden" +L["OPT_EA_LURA_ACTIONBAR"] = "Runen-Actionbar" +L["OPT_EA_LURA_ACTIONBAR_SHOW"] = "Leiste anzeigen" +L["OPT_EA_LURA_ACTIONBAR_UNLOCK"] = "Leiste entsperren" +L["OPT_EA_LURA_ACTIONBAR_AUTO_SHOW"] = "Automatisch im Bossraum anzeigen" +L["OPT_EA_LURA_ACTIONBAR_ORIENTATION"] = "Ausrichtung" +L["OPT_EA_LURA_ACTIONBAR_HORIZONTAL"] = "Horizontal" +L["OPT_EA_LURA_ACTIONBAR_VERTICAL"] = "Vertikal" +L["OPT_EA_LURA_ACTIONBAR_HINT"] = "Klicke die Runen in beobachteter Reihenfolge. Slot 5 sendet die Sequenz automatisch. Der rote Button leert die lokale Sequenz." +L["OPT_EA_LURA_ICON_SIZE"] = "Icongroesse" +L["OPT_EA_LURA_BACKGROUND_ALPHA"] = "Hintergrund-Alpha" +L["OPT_EA_LURA_ICON_SPACING"] = "Icon-Abstand" +L["OPT_EA_LURA_BORDER_ENABLED"] = "Rahmen anzeigen" +L["OPT_EA_LURA_BORDER_WIDTH"] = "Rahmenbreite" +L["OPT_EA_LURA_BORDER_COLOR"] = "Rahmenfarbe" +L["OPT_EA_LURA_SHOW_LABELS"] = "Labels anzeigen" +L["OPT_EA_LURA_RUNE_EMPTY"] = "Leer" +L["OPT_EA_LURA_DRAG_HINT"] = "Ziehen zum Verschieben" +L["OPT_EA_LURA_BOSS"] = "Boss" +L["OPT_EA_LURA_TANK"] = "Tank" L["DEVTOOLS_WINDOW_TITLE"] = "HMGT Entwicklerwerkzeuge" L["DEVTOOLS_WINDOW_HINT"] = "Strukturierte Entwickler-Ereignisse fuer die aktuelle Sitzung" L["OPT_SYNC_REMOTE_CHARGES"] = "Remote-Aufladungen synchronisieren" diff --git a/Locales/enUS.lua b/Locales/enUS.lua index cad8f90..f924893 100644 --- a/Locales/enUS.lua +++ b/Locales/enUS.lua @@ -34,7 +34,7 @@ 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." +L["VERSION_WINDOW_DEVTOOLS_ONLY"] = "HMGT: /hmgt version is only available while the debug console is enabled." -- ── Options: general ───────────────────────────────────────── L["OPT_GENERAL"] = "General" @@ -75,6 +75,36 @@ L["OPT_DEVTOOLS_OPEN"] = "Open debug console" L["OPT_DEVTOOLS_CLEAR"] = "Clear debug log" L["OPT_DEVTOOLS_SELECT_ALL"] = "Select all" L["OPT_DEVTOOLS_DISABLED"] = "HMGT: developer tools are not enabled." +L["OPT_MODULE_ENCOUNTER_ALERTS"] = "Encounter Alerts" +L["OPT_ENCOUNTER_ALERTS_PLACEHOLDER"] = "Encounter-specific helper frames and alerts." +L["OPT_EA_LURA_TITLE"] = "L'ura Runes" +L["OPT_EA_LURA_RUNE_WINDOW"] = "Rune window" +L["OPT_EA_LURA_ENABLED"] = "Enable L'ura runes" +L["OPT_EA_LURA_UNLOCK"] = "Unlock rune frame" +L["OPT_EA_LURA_HINT"] = "First version: normal/heroic layout only. Tank reference is placed bottom-center between slot 1 and 5." +L["OPT_EA_LURA_SHOW"] = "Show" +L["OPT_EA_LURA_TEST"] = "Test pattern" +L["OPT_EA_LURA_CLEAR"] = "Clear" +L["OPT_EA_LURA_BROADCAST"] = "Send sequence" +L["OPT_EA_LURA_ACTIONBAR"] = "Rune action bar" +L["OPT_EA_LURA_ACTIONBAR_SHOW"] = "Show bar" +L["OPT_EA_LURA_ACTIONBAR_UNLOCK"] = "Unlock bar" +L["OPT_EA_LURA_ACTIONBAR_AUTO_SHOW"] = "Auto show in boss room" +L["OPT_EA_LURA_ACTIONBAR_ORIENTATION"] = "Orientation" +L["OPT_EA_LURA_ACTIONBAR_HORIZONTAL"] = "Horizontal" +L["OPT_EA_LURA_ACTIONBAR_VERTICAL"] = "Vertical" +L["OPT_EA_LURA_ACTIONBAR_HINT"] = "Click rune buttons in the observed order. Slot 5 sends the sequence automatically. The red button clears the local sequence." +L["OPT_EA_LURA_ICON_SIZE"] = "Icon size" +L["OPT_EA_LURA_BACKGROUND_ALPHA"] = "Background alpha" +L["OPT_EA_LURA_ICON_SPACING"] = "Icon spacing" +L["OPT_EA_LURA_BORDER_ENABLED"] = "Show border" +L["OPT_EA_LURA_BORDER_WIDTH"] = "Border width" +L["OPT_EA_LURA_BORDER_COLOR"] = "Border color" +L["OPT_EA_LURA_SHOW_LABELS"] = "Show labels" +L["OPT_EA_LURA_RUNE_EMPTY"] = "Empty" +L["OPT_EA_LURA_DRAG_HINT"] = "Drag to move" +L["OPT_EA_LURA_BOSS"] = "Boss" +L["OPT_EA_LURA_TANK"] = "Tank" L["DEVTOOLS_WINDOW_TITLE"] = "HMGT Developer Tools" L["DEVTOOLS_WINDOW_HINT"] = "Structured developer events for the current session" L["OPT_SYNC_REMOTE_CHARGES"] = "Sync remote charges" diff --git a/Modules/EncounterAlerts/EncounterAlerts.lua b/Modules/EncounterAlerts/EncounterAlerts.lua new file mode 100644 index 0000000..cde529a --- /dev/null +++ b/Modules/EncounterAlerts/EncounterAlerts.lua @@ -0,0 +1,102 @@ +local ADDON_NAME = "HailMaryGuildTools" +local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME) +if not HMGT then return end + +local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME, true) or {} + +local EA = HMGT:NewModule("EncounterAlerts", "AceEvent-3.0") +HMGT.EncounterAlerts = EA + +EA.runtimeEnabled = false + +function EA:GetSettings() + local profile = HMGT.db and HMGT.db.profile + profile = profile or {} + profile.encounterAlerts = type(profile.encounterAlerts) == "table" and profile.encounterAlerts or {} + return profile.encounterAlerts +end + +function EA:GetLuraRunesSettings() + local settings = self:GetSettings() + settings.luraRunes = type(settings.luraRunes) == "table" and settings.luraRunes or {} + return settings.luraRunes +end + +function EA:OnEnable() + local settings = self:GetSettings() + self.runtimeEnabled = settings.enabled == true + self:RegisterEvent("PLAYER_ENTERING_WORLD", "RefreshLuraRunesContext") + self:RegisterEvent("ZONE_CHANGED", "RefreshLuraRunesContext") + self:RegisterEvent("ZONE_CHANGED_INDOORS", "RefreshLuraRunesContext") + self:RegisterEvent("ZONE_CHANGED_NEW_AREA", "RefreshLuraRunesContext") + self:RegisterEvent("GROUP_ROSTER_UPDATE", "RefreshLuraRunesContext") + self:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT", "RefreshLuraRunesContext") + self:RegisterEvent("PLAYER_TARGET_CHANGED", "RefreshLuraRunesContext") + self:RegisterEvent("ENCOUNTER_START", "HandleLuraEncounterStart") + self:RegisterEvent("ENCOUNTER_END", "HandleLuraEncounterEnd") + self:RegisterEvent("CHAT_MSG_RAID", "HandleLuraRaidChat") + self:RegisterEvent("CHAT_MSG_RAID_LEADER", "HandleLuraRaidChat") + if self.LuraRunes and self.LuraRunes.Refresh then + self.LuraRunes:Refresh() + end +end + +function EA:Enable() + local settings = self:GetSettings() + settings.enabled = true + self.runtimeEnabled = true + if self.LuraRunes and self.LuraRunes.Refresh then + self.LuraRunes:Refresh() + end +end + +function EA:Disable() + local settings = self:GetSettings() + settings.enabled = false + self.runtimeEnabled = false + if self.LuraRunes and self.LuraRunes.Hide then + self.LuraRunes:Hide() + end +end + +function EA:GetDisplayName() + return L["OPT_MODULE_ENCOUNTER_ALERTS"] or "Encounter Alerts" +end + +function EA:RefreshLuraRunesContext(event) + if self.LuraRunes and self.LuraRunes.RefreshContext then + self.LuraRunes:RefreshContext(event) + end +end + +function EA:HandleLuraEncounterStart(_, encounterId, encounterName) + if self.LuraRunes and self.LuraRunes.OnEncounterStart then + self.LuraRunes:OnEncounterStart(encounterId, encounterName) + end +end + +function EA:HandleLuraEncounterEnd(_, encounterId) + if self.LuraRunes and self.LuraRunes.OnEncounterEnd then + self.LuraRunes:OnEncounterEnd(encounterId) + end +end + +function EA:HandleLuraRunesComm(senderName, payload) + if self.LuraRunes and self.LuraRunes.HandleComm then + self.LuraRunes:HandleComm(senderName, payload) + end +end + +function EA:HandleLuraRaidChat(event, message, senderName) + if self.LuraRunes and self.LuraRunes.HandleRaidChatMessage then + self.LuraRunes:HandleRaidChatMessage(message, senderName, event) + end +end + +function EA:HandleSlashCommand(input) + if self.LuraRunes and self.LuraRunes.HandleSlashCommand then + self.LuraRunes:HandleSlashCommand(input) + else + HMGT:OpenConfig() + end +end diff --git a/Modules/EncounterAlerts/EncounterAlertsOptions.lua b/Modules/EncounterAlerts/EncounterAlertsOptions.lua new file mode 100644 index 0000000..8c9f480 --- /dev/null +++ b/Modules/EncounterAlerts/EncounterAlertsOptions.lua @@ -0,0 +1,414 @@ +local ADDON_NAME = "HailMaryGuildTools" +local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME) +if not HMGT then return end +if not HMGT_Config or not HMGT_Config.RegisterOptionsProvider then return end + +local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME, true) or {} +local AceConfigRegistry = LibStub("AceConfigRegistry-3.0", true) + +local function NotifyOptionsChanged() + if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then + AceConfigRegistry:NotifyChange(ADDON_NAME) + end +end + +local function GetSettings() + local profile = HMGT.db and HMGT.db.profile + if not profile then + return {} + end + profile.encounterAlerts = type(profile.encounterAlerts) == "table" and profile.encounterAlerts or {} + return profile.encounterAlerts +end + +local function GetLuraSettings() + local settings = GetSettings() + settings.luraRunes = type(settings.luraRunes) == "table" and settings.luraRunes or {} + settings.luraRunes.slots = type(settings.luraRunes.slots) == "table" and settings.luraRunes.slots or {} + settings.luraRunes.actionBar = type(settings.luraRunes.actionBar) == "table" and settings.luraRunes.actionBar or {} + return settings.luraRunes +end + +local function GetLuraActionBarSettings() + local settings = GetLuraSettings() + settings.actionBar.shown = settings.actionBar.shown == true + settings.actionBar.autoShow = settings.actionBar.autoShow ~= false + settings.actionBar.unlocked = settings.actionBar.unlocked == true + settings.actionBar.iconSize = tonumber(settings.actionBar.iconSize) or 42 + settings.actionBar.iconSpacing = tonumber(settings.actionBar.iconSpacing) or 8 + settings.actionBar.orientation = tostring(settings.actionBar.orientation or "horizontal") + if settings.actionBar.orientation ~= "vertical" then + settings.actionBar.orientation = "horizontal" + end + settings.actionBar.border = type(settings.actionBar.border) == "table" and settings.actionBar.border or {} + return settings.actionBar +end + +local function GetLuraBorderSettings() + local actionBar = GetLuraActionBarSettings() + actionBar.border.enabled = actionBar.border.enabled == true + actionBar.border.width = tonumber(actionBar.border.width) or 2 + actionBar.border.color = type(actionBar.border.color) == "table" and actionBar.border.color or {} + actionBar.border.color.r = tonumber(actionBar.border.color.r) or 1 + actionBar.border.color.g = tonumber(actionBar.border.color.g) or 0.82 + actionBar.border.color.b = tonumber(actionBar.border.color.b) or 0.1 + actionBar.border.color.a = tonumber(actionBar.border.color.a) or 0.9 + return actionBar.border +end + +local function GetActionBarOrientationValues() + return { + horizontal = L["OPT_EA_LURA_ACTIONBAR_HORIZONTAL"] or "Horizontal", + vertical = L["OPT_EA_LURA_ACTIONBAR_VERTICAL"] or "Vertical", + } +end + +local function GetLuraRunes() + return HMGT.EncounterAlerts and HMGT.EncounterAlerts.LuraRunes or nil +end + +local function RefreshEncounterAlerts() + if HMGT.EncounterAlerts then + local settings = GetSettings() + HMGT.EncounterAlerts.runtimeEnabled = settings.enabled == true + if HMGT.EncounterAlerts.LuraRunes and HMGT.EncounterAlerts.LuraRunes.Refresh then + HMGT.EncounterAlerts.LuraRunes:Refresh() + end + end + NotifyOptionsChanged() +end + +local function BuildRuneWindowOptions() + return { + type = "group", + inline = true, + order = 2, + name = L["OPT_EA_LURA_RUNE_WINDOW"] or "Rune window", + args = { + unlocked = { + type = "toggle", + order = 1, + width = "double", + name = L["OPT_EA_LURA_UNLOCK"] or "Unlock rune frame", + get = function() + return GetLuraSettings().unlocked == true + end, + set = function(_, value) + GetSettings().enabled = true + local settings = GetLuraSettings() + settings.enabled = true + settings.unlocked = value == true + RefreshEncounterAlerts() + end, + }, + hint = { + type = "description", + order = 2, + width = "full", + name = L["OPT_EA_LURA_HINT"] or "First version: normal/heroic layout only. Tank reference is placed bottom-center between slot 1 and 5.", + }, + show = { + type = "execute", + order = 3, + width = 0.8, + name = L["OPT_EA_LURA_SHOW"] or "Show", + func = function() + local lura = GetLuraRunes() + if lura and lura.Show then + lura:Show() + end + end, + }, + test = { + type = "execute", + order = 4, + width = 0.9, + name = L["OPT_EA_LURA_TEST"] or "Test pattern", + func = function() + local lura = GetLuraRunes() + if lura and lura.Show and lura.ApplyTestPattern then + lura:Show() + lura:ApplyTestPattern() + end + end, + }, + clear = { + type = "execute", + order = 5, + width = 0.8, + name = L["OPT_EA_LURA_CLEAR"] or "Clear", + func = function() + local lura = GetLuraRunes() + if lura and lura.ClearAssignments then + lura:ClearAssignments(false) + end + end, + }, + broadcast = { + type = "execute", + order = 6, + width = 1.2, + name = L["OPT_EA_LURA_BROADCAST"] or "Broadcast", + disabled = function() + local lura = GetLuraRunes() + return lura and lura.CanBroadcastSequence and not lura:CanBroadcastSequence() or false + end, + func = function() + local lura = GetLuraRunes() + if lura and lura.BroadcastAssignments then + lura:BroadcastAssignments() + end + end, + }, + iconSize = { + type = "range", + order = 7, + width = 1.1, + min = 28, + max = 80, + step = 1, + name = L["OPT_EA_LURA_ICON_SIZE"] or "Icon size", + get = function() + return tonumber(GetLuraSettings().iconSize) or 44 + end, + set = function(_, value) + GetLuraSettings().iconSize = tonumber(value) or 44 + RefreshEncounterAlerts() + end, + }, + backgroundAlpha = { + type = "range", + order = 8, + width = 1.1, + min = 0, + max = 0.8, + step = 0.01, + name = L["OPT_EA_LURA_BACKGROUND_ALPHA"] or "Background alpha", + get = function() + return tonumber(GetLuraSettings().backgroundAlpha) or 0.14 + end, + set = function(_, value) + GetLuraSettings().backgroundAlpha = tonumber(value) or 0.14 + RefreshEncounterAlerts() + end, + }, + }, + } +end + +local function BuildRuneActionBarOptions() + return { + type = "group", + inline = true, + order = 3, + name = L["OPT_EA_LURA_ACTIONBAR"] or "Rune action bar", + args = { + shown = { + type = "toggle", + order = 1, + width = 1.1, + name = L["OPT_EA_LURA_ACTIONBAR_SHOW"] or "Show bar", + get = function() + return GetLuraActionBarSettings().shown == true + end, + set = function(_, value) + GetSettings().enabled = true + local settings = GetLuraSettings() + settings.enabled = true + GetLuraActionBarSettings().shown = value == true + RefreshEncounterAlerts() + end, + }, + unlocked = { + type = "toggle", + order = 2, + width = 1.2, + name = L["OPT_EA_LURA_ACTIONBAR_UNLOCK"] or "Unlock bar", + get = function() + return GetLuraActionBarSettings().unlocked == true + end, + set = function(_, value) + GetSettings().enabled = true + local settings = GetLuraSettings() + settings.enabled = true + local actionBar = GetLuraActionBarSettings() + actionBar.shown = true + actionBar.unlocked = value == true + RefreshEncounterAlerts() + end, + }, + autoShow = { + type = "toggle", + order = 2.5, + width = 1.4, + name = L["OPT_EA_LURA_ACTIONBAR_AUTO_SHOW"] or "Auto show in boss room", + get = function() + return GetLuraActionBarSettings().autoShow == true + end, + set = function(_, value) + GetLuraActionBarSettings().autoShow = value == true + RefreshEncounterAlerts() + end, + }, + orientation = { + type = "select", + order = 3, + width = 1.2, + name = L["OPT_EA_LURA_ACTIONBAR_ORIENTATION"] or "Orientation", + values = GetActionBarOrientationValues, + get = function() + return GetLuraActionBarSettings().orientation + end, + set = function(_, value) + local actionBar = GetLuraActionBarSettings() + actionBar.orientation = tostring(value or "horizontal") + RefreshEncounterAlerts() + end, + }, + iconSize = { + type = "range", + order = 5, + width = 1.1, + min = 28, + max = 80, + step = 1, + name = L["OPT_EA_LURA_ICON_SIZE"] or "Icon size", + get = function() + return tonumber(GetLuraActionBarSettings().iconSize) or 42 + end, + set = function(_, value) + GetLuraActionBarSettings().iconSize = tonumber(value) or 42 + RefreshEncounterAlerts() + end, + }, + iconSpacing = { + type = "range", + order = 6, + width = 1.1, + min = 0, + max = 80, + step = 1, + name = L["OPT_EA_LURA_ICON_SPACING"] or "Icon spacing", + get = function() + return tonumber(GetLuraActionBarSettings().iconSpacing) or 8 + end, + set = function(_, value) + GetLuraActionBarSettings().iconSpacing = tonumber(value) or 8 + RefreshEncounterAlerts() + end, + }, + borderEnabled = { + type = "toggle", + order = 7, + width = 1.1, + name = L["OPT_EA_LURA_BORDER_ENABLED"] or "Show border", + get = function() + return GetLuraBorderSettings().enabled == true + end, + set = function(_, value) + GetLuraBorderSettings().enabled = value == true + RefreshEncounterAlerts() + end, + }, + borderWidth = { + type = "range", + order = 8, + width = 1.1, + min = 1, + max = 12, + step = 1, + name = L["OPT_EA_LURA_BORDER_WIDTH"] or "Border width", + disabled = function() + return GetLuraBorderSettings().enabled ~= true + end, + get = function() + return tonumber(GetLuraBorderSettings().width) or 2 + end, + set = function(_, value) + GetLuraBorderSettings().width = tonumber(value) or 2 + RefreshEncounterAlerts() + end, + }, + borderColor = { + type = "color", + order = 9, + width = 1.1, + hasAlpha = true, + name = L["OPT_EA_LURA_BORDER_COLOR"] or "Border color", + disabled = function() + return GetLuraBorderSettings().enabled ~= true + end, + get = function() + local color = GetLuraBorderSettings().color + return color.r or 1, color.g or 0.82, color.b or 0.1, color.a or 0.9 + end, + set = function(_, r, g, b, a) + local color = GetLuraBorderSettings().color + color.r = tonumber(r) or 1 + color.g = tonumber(g) or 0.82 + color.b = tonumber(b) or 0.1 + color.a = tonumber(a) or 0.9 + RefreshEncounterAlerts() + end, + }, + hint = { + type = "description", + order = 10, + width = "full", + name = L["OPT_EA_LURA_ACTIONBAR_HINT"] or "Click rune buttons in the observed order. Slot 5 sends the sequence automatically.", + }, + }, + } +end + +HMGT_Config:RegisterOptionsProvider("encounterAlerts", function() + return { + path = "encounterAlerts", + order = 50, + group = { + type = "group", + name = L["OPT_MODULE_ENCOUNTER_ALERTS"] or "Encounter Alerts", + order = 50, + childGroups = "tab", + args = { + general = { + type = "group", + name = L["OPT_GENERAL"] or "General", + order = 1, + args = { + description = { + type = "description", + order = 1, + width = "full", + name = L["OPT_ENCOUNTER_ALERTS_PLACEHOLDER"] or "Encounter-specific helper frames and alerts.", + }, + }, + }, + luraRunes = { + type = "group", + name = L["OPT_EA_LURA_TITLE"] or "L'ura Runes", + order = 2, + args = { + enabled = { + type = "toggle", + order = 1, + width = "double", + name = L["OPT_EA_LURA_ENABLED"] or "Enable L'ura runes", + get = function() + return GetLuraSettings().enabled == true + end, + set = function(_, value) + local enabled = value == true + GetSettings().enabled = enabled + GetLuraSettings().enabled = enabled + RefreshEncounterAlerts() + end, + }, + runeWindow = BuildRuneWindowOptions(), + actionBar = BuildRuneActionBarOptions(), + }, + }, + }, + }, + } +end) diff --git a/Modules/EncounterAlerts/LuraRunes.lua b/Modules/EncounterAlerts/LuraRunes.lua new file mode 100644 index 0000000..27f2100 --- /dev/null +++ b/Modules/EncounterAlerts/LuraRunes.lua @@ -0,0 +1,1113 @@ +local ADDON_NAME = "HailMaryGuildTools" +local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME) +if not HMGT then return end + +local EA = HMGT.EncounterAlerts +if not EA then return end + +local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME, true) or {} +local AceConfigRegistry = LibStub("AceConfigRegistry-3.0", true) + +local LR = {} +EA.LuraRunes = LR + +local MEDIA_DIR = "Interface\\AddOns\\HailMaryGuildTools\\Modules\\EncounterAlerts\\Media\\LuraRunes\\" +local FALLBACK_TEXTURE = "Interface\\AddOns\\HailMaryGuildTools\\Media\\HailMaryIcon.png" +local ROLE_ICON_TEXTURE = "Interface\\LFGFrame\\UI-LFG-ICON-ROLES" +local CLEAR_BUTTON_TEXTURE = "Interface\\Buttons\\UI-GroupLoot-Pass-Up" + +local RUNE_ORDER = { "circle", "cross", "diamond", "t", "triangle" } +local RUNE_DATA = { + circle = { + label = "Circle", + chatToken = "Rune_Circle", + texture = MEDIA_DIR .. "Rune_Circle.tga", + aliases = { "kreis", "round", "rund" }, + }, + cross = { + label = "Cross", + chatToken = "Rune_X", + texture = MEDIA_DIR .. "Rune_X.tga", + aliases = { "x", "kreuz" }, + }, + diamond = { + label = "Diamond", + chatToken = "Rune_Diamond", + texture = MEDIA_DIR .. "Rune_Diamond.tga", + aliases = { "diamant", "rhombus" }, + }, + t = { + label = "T", + chatToken = "Rune_T", + texture = MEDIA_DIR .. "Rune_T.tga", + aliases = { "tee" }, + }, + triangle = { + label = "Triangle", + chatToken = "Rune_Triangle", + texture = MEDIA_DIR .. "Rune_Triangle.tga", + aliases = { "dreieck" }, + }, +} + +local RUNE_ALIASES = {} +for key, data in pairs(RUNE_DATA) do + RUNE_ALIASES[key] = key + if data.chatToken then + RUNE_ALIASES[string.lower(data.chatToken)] = key + end + for _, alias in ipairs(data.aliases or {}) do + RUNE_ALIASES[alias] = key + end +end + +local DEFAULT_TEST_ASSIGNMENTS = { + "circle", + "cross", + "diamond", + "t", + "triangle", +} +local LURA_NAME_TOKENS = { "l'ura", "lura" } +local LURA_CONTEXT_MAP_IDS = {} +local LURA_ENCOUNTER_IDS = {} +local LURA_NPC_IDS = {} +local LURA_SCAN_UNITS = { "boss1", "boss2", "boss3", "boss4", "boss5", "target", "focus" } + +local function Debug(level, fmt, ...) + if HMGT and HMGT.DebugScoped then + HMGT:DebugScoped(level or "info", "EncounterAlerts", fmt, ...) + end +end + +local function NotifyOptionsChanged() + if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then + AceConfigRegistry:NotifyChange(ADDON_NAME) + end +end + +local function ClampNumber(value, minimum, maximum, fallback) + local number = tonumber(value) + if not number then + number = fallback + end + number = tonumber(number) or 0 + if number < minimum then return minimum end + if number > maximum then return maximum end + return number +end + +local function NormalizeRuneKey(value) + local key = tostring(value or ""):lower() + return RUNE_ALIASES[key] or "" +end + +local function ParseRuneRaidChatMessage(message) + local okText, text = pcall(tostring, message) + if not okText or type(text) ~= "string" then + return nil, nil + end + + local okMatch, slotText, token = pcall(string.match, text, "^HMGT:Rune([1-5]):([%w_%-]+)$") + if not okMatch or not slotText or not token then + return nil, nil + end + + local key = NormalizeRuneKey(token) + if key == "" then + return nil, nil + end + + return tonumber(slotText), key +end + +local function NormalizeActionBarOrientation(value) + if tostring(value or "") == "vertical" then + return "vertical" + end + return "horizontal" +end + +local function TextLooksLikeLura(text) + local value = tostring(text or ""):lower() + if value == "" then + return false + end + for _, token in ipairs(LURA_NAME_TOKENS) do + if string.find(value, token, 1, true) then + return true + end + end + if string.find((value:gsub("%W", "")), "lura", 1, true) then + return true + end + return false +end + +local function GetNPCIdFromGUID(guid) + local _, _, _, _, _, npcId = strsplit("-", tostring(guid or "")) + return tonumber(npcId) +end + +local function UnitLooksLikeLura(unitId) + if not unitId or not UnitExists(unitId) then + return false + end + + local npcId = GetNPCIdFromGUID(UnitGUID(unitId)) + if npcId and LURA_NPC_IDS[npcId] then + return true + end + + return TextLooksLikeLura(UnitName(unitId)) +end + +local function NormalizeColor(color, fallback) + color = type(color) == "table" and color or {} + fallback = type(fallback) == "table" and fallback or {} + return { + r = ClampNumber(color.r, 0, 1, fallback.r or 1), + g = ClampNumber(color.g, 0, 1, fallback.g or 0.82), + b = ClampNumber(color.b, 0, 1, fallback.b or 0.1), + a = ClampNumber(color.a, 0, 1, fallback.a or 0.9), + } +end + +local function ApplyIconBorder(frame, settings, backgroundColor) + if not frame or type(frame.SetBackdrop) ~= "function" then + return + end + + local border = settings and settings.border or {} + if border.enabled ~= true then + frame:SetBackdrop(nil) + return + end + + local width = math.max(1, tonumber(border.width) or 2) + frame:SetBackdrop({ + bgFile = "Interface\\Buttons\\WHITE8X8", + edgeFile = "Interface\\Buttons\\WHITE8X8", + edgeSize = width, + insets = { left = width, right = width, top = width, bottom = width }, + }) + + local bg = backgroundColor or { r = 0, g = 0, b = 0, a = 0.35 } + local color = border.color or {} + frame:SetBackdropColor(bg.r or 0, bg.g or 0, bg.b or 0, bg.a or 0.35) + frame:SetBackdropBorderColor(color.r or 1, color.g or 0.82, color.b or 0.1, color.a or 0.9) +end + +local function SetTankIconTexture(texture) + if not texture then + return + end + + if type(texture.SetAtlas) == "function" then + local ok = pcall(texture.SetAtlas, texture, "roleicon-tiny-tank", true) + if ok then + return + end + end + + texture:SetTexture(ROLE_ICON_TEXTURE) + if type(GetTexCoordsForRoleSmallCircle) == "function" then + local left, right, top, bottom = GetTexCoordsForRoleSmallCircle("TANK") + if left and right and top and bottom then + texture:SetTexCoord(left, right, top, bottom) + return + end + elseif type(GetTexCoordsForRole) == "function" then + local left, right, top, bottom = GetTexCoordsForRole("TANK") + if left and right and top and bottom then + texture:SetTexCoord(left, right, top, bottom) + return + end + end + + texture:SetTexCoord(0, 0.26171875, 0.26171875, 0.5234375) +end + +local function HasAssignments(slots) + if type(slots) ~= "table" then + return false + end + for slot = 1, 5 do + if NormalizeRuneKey(slots[slot]) ~= "" then + return true + end + end + return false +end + +local function UnitCanSendSequence(unitId) + if not unitId or not UnitExists(unitId) then + return false + end + if UnitIsGroupLeader and UnitIsGroupLeader(unitId) then + return true + end + if UnitIsGroupAssistant and UnitIsGroupAssistant(unitId) then + return true + end + return false +end + +function LR:IsSequenceSenderAllowed(playerName) + if playerName and playerName ~= "" then + local unitId = HMGT and HMGT.GetUnitForPlayer and HMGT:GetUnitForPlayer(playerName) or nil + return UnitCanSendSequence(unitId) + end + if not IsInGroup() and not IsInRaid() then + return true + end + return UnitCanSendSequence("player") +end + +function LR:CanBroadcastSequence() + return self:IsSequenceSenderAllowed(nil) +end + +function LR:IsTestMode() + return self.testMode == true +end + +function LR:CanUseRuneInput() + return self:IsTestMode() or self:CanBroadcastSequence() +end + +function LR:IsLuraEncounter(encounterId, encounterName) + local id = tonumber(encounterId) or 0 + if LURA_ENCOUNTER_IDS[id] then + return true + end + return TextLooksLikeLura(encounterName) +end + +function LR:HasLuraUnit() + for _, unitId in ipairs(LURA_SCAN_UNITS) do + if UnitLooksLikeLura(unitId) then + return true + end + end + return false +end + +function LR:IsInLuraContext() + if self.luraEncounterActive == true then + return true + end + + local inInstance, instanceType = IsInInstance() + if inInstance ~= true or instanceType ~= "raid" then + return false + end + + local mapId = C_Map and C_Map.GetBestMapForUnit and C_Map.GetBestMapForUnit("player") or nil + if mapId and LURA_CONTEXT_MAP_IDS[mapId] then + return true + end + local mapInfo = mapId and C_Map and C_Map.GetMapInfo and C_Map.GetMapInfo(mapId) or nil + if mapInfo and TextLooksLikeLura(mapInfo.name) then + return true + end + + if TextLooksLikeLura(GetSubZoneText and GetSubZoneText() or nil) + or TextLooksLikeLura(GetMinimapZoneText and GetMinimapZoneText() or nil) + or TextLooksLikeLura(GetRealZoneText and GetRealZoneText() or nil) then + return true + end + + return self:HasLuraUnit() +end + +function LR:ShouldAutoShowActionBar() + local settings = self:GetSettings() + return settings.actionBar.autoShow == true + and self:CanBroadcastSequence() + and self:IsInLuraContext() +end + +function LR:RefreshContext(reason) + local contextActive = self:IsInLuraContext() + local canUse = self:CanUseRuneInput() + local autoShow = self:ShouldAutoShowActionBar() + if contextActive ~= self.lastContextActive or canUse ~= self.lastCanUse or autoShow ~= self.lastAutoShow then + Debug( + "verbose", + "Lura context reason=%s active=%s autoShow=%s canUse=%s", + tostring(reason or "refresh"), + tostring(contextActive), + tostring(autoShow), + tostring(canUse) + ) + self.lastContextActive = contextActive + self.lastCanUse = canUse + self.lastAutoShow = autoShow + end + self:RefreshActionBar() +end + +function LR:OnEncounterStart(encounterId, encounterName) + self.luraEncounterActive = self:IsLuraEncounter(encounterId, encounterName) + if self.luraEncounterActive then + Debug("info", "Lura encounter context started encounter=%s", tostring(encounterId or "?")) + end + self:RefreshContext("encounter_start") +end + +function LR:OnEncounterEnd(encounterId) + if self.luraEncounterActive then + Debug("info", "Lura encounter context ended encounter=%s", tostring(encounterId or "?")) + end + self.luraEncounterActive = false + self:RefreshContext("encounter_end") +end + +function LR:LogNotLeader(context) + Debug("info", "Lura rune %s blocked: only raid leader or raid assist can send", tostring(context or "sequence")) +end + +local function SplitAssignments(payload) + local slots = {} + local text = tostring(payload or "") + local index = 1 + for token in string.gmatch(text .. ",", "([^,]*),") do + if index > 5 then + break + end + slots[index] = NormalizeRuneKey(token) + index = index + 1 + end + for slot = 1, 5 do + slots[slot] = NormalizeRuneKey(slots[slot]) + end + return slots +end + +function LR:GetSettings() + local settings = EA:GetLuraRunesSettings() + settings.enabled = settings.enabled == true + settings.unlocked = settings.unlocked == true + settings.posX = math.floor(ClampNumber(settings.posX, -1200, 1200, 0) + 0.5) + settings.posY = math.floor(ClampNumber(settings.posY, -900, 900, -120) + 0.5) + settings.iconSize = math.floor(ClampNumber(settings.iconSize, 28, 80, 44) + 0.5) + settings.backgroundAlpha = ClampNumber(settings.backgroundAlpha, 0, 0.8, 0.14) + settings.showLabels = settings.showLabels ~= false + settings.actionBar = type(settings.actionBar) == "table" and settings.actionBar or {} + settings.actionBar.shown = settings.actionBar.shown == true + settings.actionBar.autoShow = settings.actionBar.autoShow ~= false + settings.actionBar.unlocked = settings.actionBar.unlocked == true + settings.actionBar.posX = math.floor(ClampNumber(settings.actionBar.posX, -1200, 1200, 0) + 0.5) + settings.actionBar.posY = math.floor(ClampNumber(settings.actionBar.posY, -900, 900, -300) + 0.5) + settings.actionBar.iconSize = math.floor(ClampNumber(settings.actionBar.iconSize, 28, 80, 42) + 0.5) + settings.actionBar.iconSpacing = math.floor(ClampNumber(settings.actionBar.iconSpacing, 0, 80, 8) + 0.5) + settings.actionBar.orientation = NormalizeActionBarOrientation(settings.actionBar.orientation) + settings.actionBar.border = type(settings.actionBar.border) == "table" and settings.actionBar.border or {} + settings.actionBar.border.enabled = settings.actionBar.border.enabled == true + settings.actionBar.border.width = math.floor(ClampNumber(settings.actionBar.border.width, 1, 12, 2) + 0.5) + settings.actionBar.border.color = NormalizeColor(settings.actionBar.border.color, { r = 1, g = 0.82, b = 0.1, a = 0.9 }) + settings.slots = type(settings.slots) == "table" and settings.slots or {} + for slot = 1, 5 do + settings.slots[slot] = NormalizeRuneKey(settings.slots[slot]) + end + return settings +end + +function LR:GetRuneLabel(key) + local normalized = NormalizeRuneKey(key) + local data = RUNE_DATA[normalized] + if data then + return data.label + end + return L["OPT_EA_LURA_RUNE_EMPTY"] or "Empty" +end + +function LR:GetRuneTexture(key) + local normalized = NormalizeRuneKey(key) + local data = RUNE_DATA[normalized] + return (data and data.texture) or nil +end + +function LR:GetRuneChatToken(key) + local normalized = NormalizeRuneKey(key) + local data = RUNE_DATA[normalized] + return (data and data.chatToken) or normalized +end + +function LR:GetAssignmentsSummary() + local settings = self:GetSettings() + local parts = {} + for slot = 1, 5 do + parts[#parts + 1] = string.format("%d=%s", slot, self:GetRuneLabel(settings.slots[slot])) + end + return table.concat(parts, ", ") +end + +function LR:SerializeAssignments() + local settings = self:GetSettings() + local parts = {} + for slot = 1, 5 do + parts[slot] = NormalizeRuneKey(settings.slots[slot]) + end + return table.concat(parts, ",") +end + +function LR:SavePosition() + local frame = self.frame + if not frame then + return + end + local frameCenterX, frameCenterY = frame:GetCenter() + local parentCenterX, parentCenterY = UIParent:GetCenter() + if not frameCenterX or not frameCenterY or not parentCenterX or not parentCenterY then + return + end + local settings = self:GetSettings() + settings.posX = math.floor(frameCenterX - parentCenterX + 0.5) + settings.posY = math.floor(frameCenterY - parentCenterY + 0.5) +end + +function LR:SaveActionBarPosition() + local frame = self.actionBarFrame + if not frame then + return + end + local frameCenterX, frameCenterY = frame:GetCenter() + local parentCenterX, parentCenterY = UIParent:GetCenter() + if not frameCenterX or not frameCenterY or not parentCenterX or not parentCenterY then + return + end + local settings = self:GetSettings() + settings.actionBar.posX = math.floor(frameCenterX - parentCenterX + 0.5) + settings.actionBar.posY = math.floor(frameCenterY - parentCenterY + 0.5) +end + +function LR:ApplyFrameStyle() + local frame = self.frame + if not frame then + return + end + + local settings = self:GetSettings() + frame:SetScale(1) + frame:ClearAllPoints() + frame:SetPoint("CENTER", UIParent, "CENTER", settings.posX or 0, settings.posY or -120) + frame:EnableMouse(settings.unlocked == true) + + if type(frame.SetBackdrop) == "function" then + local backgroundAlpha = settings.backgroundAlpha or 0.14 + if settings.unlocked == true then + frame:SetBackdrop({ + bgFile = "Interface\\Buttons\\WHITE8X8", + edgeFile = "Interface\\Buttons\\WHITE8X8", + edgeSize = 1, + insets = { left = 1, right = 1, top = 1, bottom = 1 }, + }) + frame:SetBackdropColor(0, 0, 0, backgroundAlpha) + frame:SetBackdropBorderColor(0, 0, 0, 1) + elseif backgroundAlpha > 0 then + frame:SetBackdrop({ + bgFile = "Interface\\Buttons\\WHITE8X8", + }) + frame:SetBackdropColor(0, 0, 0, backgroundAlpha) + else + frame:SetBackdrop(nil) + end + end + if frame.dragHint then + frame.dragHint:SetShown(settings.unlocked == true) + end + + local iconSize = settings.iconSize or 44 + local iconSpacing = 8 + local slotSize = iconSize + local radiusX = math.max(116, (iconSize + iconSpacing) * 2.35) + local radiusY = math.max(104, (iconSize + iconSpacing) * 2.15) + frame:SetSize(math.max(340, radiusX * 2.25 + slotSize), math.max(300, radiusY * 2.1 + slotSize)) + local positions = { + [1] = { -radiusX * 0.56, -radiusY * 0.70 }, + [2] = { -radiusX, radiusY * 0.08 }, + [3] = { 0, radiusY }, + [4] = { radiusX, radiusY * 0.08 }, + [5] = { radiusX * 0.56, -radiusY * 0.70 }, + } + + for slot = 1, 5 do + local slotFrame = frame.slots and frame.slots[slot] + if slotFrame then + slotFrame:ClearAllPoints() + slotFrame:SetPoint("CENTER", frame, "CENTER", positions[slot][1], positions[slot][2]) + slotFrame:SetSize(slotSize, slotSize) + slotFrame.icon:SetSize(iconSize, iconSize) + if type(slotFrame.SetBackdrop) == "function" then + slotFrame:SetBackdrop(nil) + end + slotFrame.label:SetShown(false) + end + end + + if frame.tank then + frame.tank:ClearAllPoints() + frame.tank:SetPoint("CENTER", frame, "CENTER", 0, -radiusY * 0.98) + frame.tank:SetSize(math.max(28, iconSize * 0.72), math.max(28, iconSize * 0.72)) + frame.tank.icon:SetAllPoints(frame.tank) + end +end + +function LR:UpdateSlot(slot) + local frame = self.frame + local slotFrame = frame and frame.slots and frame.slots[slot] + if not slotFrame then + return + end + + local settings = self:GetSettings() + local key = NormalizeRuneKey(settings.slots[slot]) + local texture = self:GetRuneTexture(key) + if texture then + slotFrame.icon:SetTexture(texture) + slotFrame.icon:SetVertexColor(1, 1, 1, 1) + else + slotFrame.icon:SetTexture(FALLBACK_TEXTURE) + slotFrame.icon:SetVertexColor(0.18, 0.18, 0.18, 0.45) + end + slotFrame.label:SetText("") +end + +function LR:UpdateFrame() + if not self.frame then + return + end + self:ApplyFrameStyle() + for slot = 1, 5 do + self:UpdateSlot(slot) + end +end + +function LR:ApplyActionBarStyle() + local frame = self.actionBarFrame + if not frame then + return + end + + local settings = self:GetSettings() + local actionBar = settings.actionBar + local iconSpacing = actionBar.iconSpacing or 8 + local borderWidth = actionBar.border and actionBar.border.enabled and (actionBar.border.width or 2) or 0 + frame:SetScale(1) + frame:ClearAllPoints() + frame:SetPoint("CENTER", UIParent, "CENTER", actionBar.posX or 0, actionBar.posY or -300) + frame:EnableMouse(actionBar.unlocked == true) + if type(frame.SetBackdrop) == "function" then + if actionBar.unlocked == true then + frame:SetBackdrop({ + bgFile = "Interface\\Buttons\\WHITE8X8", + edgeFile = "Interface\\Buttons\\WHITE8X8", + edgeSize = 1, + insets = { left = 1, right = 1, top = 1, bottom = 1 }, + }) + frame:SetBackdropColor(0, 0, 0, 0.28) + frame:SetBackdropBorderColor(0, 0, 0, 1) + else + frame:SetBackdrop(nil) + end + end + if frame.dragHint then + frame.dragHint:SetShown(actionBar.unlocked == true) + end + + local buttons = frame.buttons or {} + local iconSize = actionBar.iconSize or 42 + local buttonSize = iconSize + (borderWidth * 2) + local spacing = iconSpacing + local padding = 8 + local count = math.max(1, #buttons) + local orientation = NormalizeActionBarOrientation(actionBar.orientation) + if orientation == "vertical" then + frame:SetSize(buttonSize + padding, (count * buttonSize) + ((count - 1) * spacing) + padding) + else + frame:SetSize((count * buttonSize) + ((count - 1) * spacing) + padding, buttonSize + padding) + end + + local totalLength = (count * buttonSize) + ((count - 1) * spacing) + local start = -(totalLength - buttonSize) / 2 + for index, button in ipairs(buttons) do + button:SetSize(buttonSize, buttonSize) + if button.icon then + button.icon:SetSize(iconSize, iconSize) + end + ApplyIconBorder(button, actionBar, button.isClear and { r = 0.18, g = 0, b = 0, a = 0.48 } or nil) + button:ClearAllPoints() + if orientation == "vertical" then + button:SetPoint("CENTER", frame, "CENTER", 0, -start - ((index - 1) * (buttonSize + spacing))) + else + button:SetPoint("CENTER", frame, "CENTER", start + ((index - 1) * (buttonSize + spacing)), 0) + end + end + + local canUse = self:CanUseRuneInput() + for _, button in ipairs(buttons) do + if type(button.SetEnabled) == "function" then + button:SetEnabled(canUse) + end + button:SetAlpha(canUse and 1 or 0.45) + end +end + +function LR:ShouldShowActionBar() + local settings = self:GetSettings() + return EA.runtimeEnabled == true + and settings.enabled == true + and (settings.actionBar.shown == true or self:IsTestMode() or self:ShouldAutoShowActionBar()) +end + +function LR:RefreshActionBar() + local frame = self.actionBarFrame + if not frame and not self:ShouldShowActionBar() then + return + end + frame = self:EnsureActionBar() + self:ApplyActionBarStyle() + if self:ShouldShowActionBar() then + frame:Show() + else + frame:Hide() + end +end + +function LR:EnsureActionBar() + if self.actionBarFrame then + return self.actionBarFrame + end + + local frame = CreateFrame("Frame", "HMGT_LuraRuneActionBar", UIParent, "BackdropTemplate") + frame:SetSize(316, 58) + frame:SetFrameStrata("FULLSCREEN_DIALOG") + frame:SetFrameLevel(205) + frame:SetClampedToScreen(true) + frame:SetMovable(true) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", function(selfFrame) + if LR:GetSettings().actionBar.unlocked == true then + selfFrame:StartMoving() + end + end) + frame:SetScript("OnDragStop", function(selfFrame) + selfFrame:StopMovingOrSizing() + LR:SaveActionBarPosition() + LR:ApplyActionBarStyle() + end) + frame:Hide() + + local dragHint = frame:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall") + dragHint:SetPoint("TOP", frame, "BOTTOM", 0, -2) + dragHint:SetText(L["OPT_EA_LURA_DRAG_HINT"] or "Drag to move") + frame.dragHint = dragHint + + frame.buttons = {} + local size = 42 + for _, key in ipairs(RUNE_ORDER) do + local data = RUNE_DATA[key] or {} + local button = CreateFrame("Button", nil, frame, "BackdropTemplate") + button:SetSize(size, size) + + button.icon = button:CreateTexture(nil, "ARTWORK") + button.icon:SetPoint("CENTER") + button.icon:SetSize(size - 8, size - 8) + button.icon:SetTexture(data.texture or FALLBACK_TEXTURE) + button.icon:SetVertexColor(1, 1, 1, 1) + + button:SetScript("OnClick", function() + LR:AppendRuneToSequence(key) + LR:ApplyActionBarStyle() + end) + frame.buttons[#frame.buttons + 1] = button + end + + local clearButton = CreateFrame("Button", nil, frame, "BackdropTemplate") + clearButton:SetSize(size, size) + clearButton.isClear = true + clearButton.icon = clearButton:CreateTexture(nil, "ARTWORK") + clearButton.icon:SetPoint("CENTER") + clearButton.icon:SetSize(size - 8, size - 8) + clearButton.icon:SetTexture(CLEAR_BUTTON_TEXTURE) + clearButton.icon:SetVertexColor(1, 1, 1, 1) + clearButton:SetScript("OnClick", function() + LR:ClearAssignments(false) + LR:ApplyActionBarStyle() + end) + frame.clearButton = clearButton + frame.buttons[#frame.buttons + 1] = clearButton + + self.actionBarFrame = frame + self:ApplyActionBarStyle() + return frame +end + +function LR:EnsureFrame() + if self.frame then + return self.frame + end + + local frame = CreateFrame("Frame", "HMGT_LuraRunesFrame", UIParent, "BackdropTemplate") + frame:SetSize(340, 300) + frame:SetFrameStrata("FULLSCREEN_DIALOG") + frame:SetFrameLevel(190) + frame:SetClampedToScreen(true) + frame:SetMovable(true) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", function(selfFrame) + if LR:GetSettings().unlocked == true then + selfFrame:StartMoving() + end + end) + frame:SetScript("OnDragStop", function(selfFrame) + selfFrame:StopMovingOrSizing() + LR:SavePosition() + LR:ApplyFrameStyle() + end) + frame:Hide() + + local dragHint = frame:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall") + dragHint:SetPoint("TOP", frame, "TOP", 0, -4) + dragHint:SetText(L["OPT_EA_LURA_DRAG_HINT"] or "Drag to move") + frame.dragHint = dragHint + + local boss = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") + boss:SetPoint("CENTER", frame, "CENTER", 0, 0) + boss:SetText(L["OPT_EA_LURA_BOSS"] or "Boss") + frame.boss = boss + + local tank = CreateFrame("Frame", nil, frame) + tank:SetSize(32, 32) + tank.icon = tank:CreateTexture(nil, "ARTWORK") + tank.icon:SetAllPoints(tank) + SetTankIconTexture(tank.icon) + frame.tank = tank + + frame.slots = {} + for slot = 1, 5 do + local slotFrame = CreateFrame("Frame", nil, frame, "BackdropTemplate") + + slotFrame.icon = slotFrame:CreateTexture(nil, "ARTWORK") + slotFrame.icon:SetPoint("CENTER", slotFrame, "CENTER", 0, 0) + slotFrame.icon:SetTexture(FALLBACK_TEXTURE) + + slotFrame.number = slotFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + slotFrame.number:SetPoint("TOPLEFT", slotFrame, "TOPLEFT", 5, -3) + slotFrame.number:SetText(tostring(slot)) + + slotFrame.label = slotFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall") + slotFrame.label:SetPoint("TOP", slotFrame.icon, "BOTTOM", 0, -2) + slotFrame.label:SetWidth(90) + slotFrame.label:SetJustifyH("CENTER") + slotFrame.label:SetText("") + slotFrame.label:Hide() + + frame.slots[slot] = slotFrame + end + + self.frame = frame + self:UpdateFrame() + return frame +end + +function LR:ShouldShow() + local settings = self:GetSettings() + return EA.runtimeEnabled == true + and settings.enabled == true + and (settings.unlocked == true or HasAssignments(settings.slots)) +end + +function LR:Refresh() + local frame = self:EnsureFrame() + self:UpdateFrame() + if self:ShouldShow() then + frame:Show() + else + frame:Hide() + end + self:RefreshActionBar() +end + +function LR:Show() + local settings = self:GetSettings() + EA:GetSettings().enabled = true + EA.runtimeEnabled = true + settings.enabled = true + self:EnsureFrame() + self:UpdateFrame() + self.frame:Show() + NotifyOptionsChanged() +end + +function LR:Hide() + self.testMode = false + if self.frame then + self.frame:Hide() + end + if self.actionBarFrame then + self.actionBarFrame:Hide() + end +end + +function LR:ApplyAssignments(slots, source) + local settings = self:GetSettings() + for slot = 1, 5 do + settings.slots[slot] = NormalizeRuneKey(slots and slots[slot]) + end + self:Refresh() + NotifyOptionsChanged() + Debug("info", "Lura runes updated source=%s %s", tostring(source or "local"), self:GetAssignmentsSummary()) +end + +function LR:ApplyTestPattern() + self.testMode = true + Debug("info", "Lura local test mode enabled") + self:ApplyAssignments(DEFAULT_TEST_ASSIGNMENTS, "test") +end + +function LR:ClearAssignments(broadcast) + self:ApplyAssignments({}, "clear") + if broadcast then + self:BroadcastAssignments() + end +end + +function LR:GetNextEmptySequenceSlot() + local settings = self:GetSettings() + for slot = 1, 5 do + if NormalizeRuneKey(settings.slots[slot]) == "" then + return slot + end + end + return nil +end + +function LR:LogSequenceProgress(slot, key) + local label = self:GetRuneLabel(key) + if slot >= 5 then + Debug("info", "Lura rune sequence complete %s", self:GetAssignmentsSummary()) + else + Debug("info", "Lura rune %s saved as slot %d; waiting for next rune", tostring(label or "?"), slot) + end +end + +function LR:SendRuneRaidChat(slot, key) + if self:IsTestMode() or not self:CanBroadcastSequence() then + return false + end + if not IsInRaid() then + Debug("verbose", "Lura raid chat skipped: player is not in raid") + return false + end + + local slotIndex = tonumber(slot) or 0 + if slotIndex < 1 or slotIndex > 5 then + return false + end + + local token = self:GetRuneChatToken(key) + if not token or token == "" then + return false + end + + local message = string.format("HMGT:Rune%d:%s", slotIndex, token) + local ok = false + if C_ChatInfo and type(C_ChatInfo.SendChatMessage) == "function" then + ok = pcall(C_ChatInfo.SendChatMessage, message, "RAID") + elseif type(SendChatMessage) == "function" then + ok = pcall(SendChatMessage, message, "RAID") + end + + if ok then + Debug("info", "Lura raid chat sent %s", message) + return true + end + + Debug("error", "Lura raid chat failed %s", message) + return false +end + +function LR:SendRuneRaidChatSequence() + local settings = self:GetSettings() + local sent = 0 + for slot = 1, 5 do + local key = NormalizeRuneKey(settings.slots[slot]) + if key ~= "" and self:SendRuneRaidChat(slot, key) then + sent = sent + 1 + end + end + return sent +end + +function LR:AppendRuneToSequence(key) + local runeKey = NormalizeRuneKey(key) + if runeKey == "" then + Debug("info", "Lura rune input ignored: unknown rune. Valid runes: circle, x, diamond, t, triangle") + return false + end + if not self:CanUseRuneInput() then + self:LogNotLeader("input") + return false + end + + local settings = self:GetSettings() + local slot = self:GetNextEmptySequenceSlot() + if not slot then + for index = 1, 5 do + settings.slots[index] = "" + end + slot = 1 + end + + EA:GetSettings().enabled = true + EA.runtimeEnabled = true + settings.enabled = true + settings.slots[slot] = runeKey + self:Refresh() + NotifyOptionsChanged() + self:LogSequenceProgress(slot, runeKey) + Debug("info", "Lura rune input slot=%d rune=%s %s", slot, tostring(runeKey), self:GetAssignmentsSummary()) + self:SendRuneRaidChat(slot, runeKey) + + if slot >= 5 and self:CanBroadcastSequence() then + self:BroadcastAssignments(false) + elseif slot >= 5 then + Debug("info", "Lura local test sequence complete; not sending to raid") + end + return true +end + +function LR:BroadcastAssignments(sendRaidChat) + if not self:CanBroadcastSequence() then + self:LogNotLeader("sequence send") + return false + end + + local prefix = HMGT.MSG_LURA_RUNES or "LUR" + local payload = self:SerializeAssignments() + if sendRaidChat ~= false then + self:SendRuneRaidChatSequence() + end + HMGT:SendGroupMessage(string.format("%s|%s", prefix, payload), "ALERT") + Debug("info", "Lura rune sequence sent %s", self:GetAssignmentsSummary()) + return true +end + +function LR:HandleComm(senderName, payload) + local settings = self:GetSettings() + if EA.runtimeEnabled ~= true or settings.enabled ~= true then + return + end + if not self:IsSequenceSenderAllowed(senderName) then + Debug("info", "Lura rune sequence ignored from non-leader/non-assist sender=%s", tostring(senderName or "?")) + return + end + self:ApplyAssignments(SplitAssignments(payload), senderName) +end + +function LR:HandleRaidChatMessage(message, senderName, event) + local slot, key = ParseRuneRaidChatMessage(message) + if not slot or not key then + return false + end + + local settings = self:GetSettings() + if EA.runtimeEnabled ~= true or settings.enabled ~= true then + Debug("verbose", "Lura raid chat ignored while disabled event=%s sender=%s", tostring(event or "?"), tostring(senderName or "?")) + return false + end + + if not self:IsSequenceSenderAllowed(senderName) then + Debug("info", "Lura raid chat ignored from non-leader/non-assist sender=%s", tostring(senderName or "?")) + return false + end + + settings.slots[slot] = key + self:Refresh() + NotifyOptionsChanged() + Debug("info", "Lura raid chat applied sender=%s slot=%d rune=%s", tostring(senderName or "?"), slot, tostring(key)) + return true +end + +function LR:HandleSlashCommand(input) + local rest = tostring(input or ""):match("^lura%s*(.*)$") or "" + rest = rest:gsub("^%s+", ""):gsub("%s+$", "") + if rest == "" or rest == "show" then + self:Show() + return + end + if rest == "hide" then + self:Hide() + return + end + if rest == "unlock" then + local settings = self:GetSettings() + settings.unlocked = not settings.unlocked + self:Show() + self:Refresh() + return + end + if rest == "bar" or rest == "buttons" or rest == "actionbar" then + local settings = self:GetSettings() + EA:GetSettings().enabled = true + EA.runtimeEnabled = true + settings.enabled = true + settings.actionBar.shown = not settings.actionBar.shown + self:Refresh() + NotifyOptionsChanged() + return + end + if rest == "bar unlock" or rest == "buttons unlock" or rest == "actionbar unlock" then + local settings = self:GetSettings() + EA:GetSettings().enabled = true + EA.runtimeEnabled = true + settings.enabled = true + settings.actionBar.shown = true + settings.actionBar.unlocked = not settings.actionBar.unlocked + self:Refresh() + NotifyOptionsChanged() + return + end + if rest == "test" then + self:Show() + self:ApplyTestPattern() + return + end + if rest == "send" or rest == "broadcast" then + self:BroadcastAssignments() + return + end + if rest == "clear" then + self:ClearAssignments(true) + return + end + if rest == "reset" or rest == "new" then + self.testMode = false + self:ClearAssignments(false) + return + end + + local slots = {} + local index = 1 + for token in rest:gmatch("[^,%s]+") do + if index > 5 then + break + end + slots[index] = NormalizeRuneKey(token) + index = index + 1 + end + if index == 2 then + self:AppendRuneToSequence(slots[1]) + return + end + if index > 1 then + self:Show() + self:ApplyAssignments(slots, "slash") + self:BroadcastAssignments() + else + Debug("info", "Lura slash usage: /hmgt lura test | reset | clear | unlock | send | circle x diamond t triangle") + end +end diff --git a/Modules/EncounterAlerts/Media/LuraRunes/.gitkeep b/Modules/EncounterAlerts/Media/LuraRunes/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Modules/EncounterAlerts/Media/LuraRunes/.gitkeep @@ -0,0 +1 @@ + diff --git a/Modules/EncounterAlerts/Media/LuraRunes/Rune_Circle.tga b/Modules/EncounterAlerts/Media/LuraRunes/Rune_Circle.tga new file mode 100644 index 0000000000000000000000000000000000000000..5610e4c06b60f7be57a955687f37760db94f4b1f GIT binary patch literal 65554 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

oZCkaNe*w|9_g@b|RH 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 Lz-xh0ZGry=b;^(y literal 0 HcmV?d00001 diff --git a/Modules/EncounterAlerts/Media/LuraRunes/Rune_Triangle.tga b/Modules/EncounterAlerts/Media/LuraRunes/Rune_Triangle.tga new file mode 100644 index 0000000000000000000000000000000000000000..6773d791829f3413403f0eda3a07628399b2ee2d GIT binary patch literal 65554 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 literal 0 HcmV?d00001 diff --git a/Modules/EncounterAlerts/Media/LuraRunes/Rune_X.tga b/Modules/EncounterAlerts/Media/LuraRunes/Rune_X.tga new file mode 100644 index 0000000000000000000000000000000000000000..20f3537fd3464beede53cbccb7875fcae556be37 GIT binary patch literal 65554 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 L'ura Runes > Rune action bar` + Shows five clickable rune buttons plus a local clear button for building the sequence +- `/hmgt lura reset` + Clears the local L'ura sequence builder +- `/hmgt lura bar` + Toggles the L'ura rune action bar - `/hmgt debug` - Opens the developer tools window + Opens the debug console - `/hmgt dev` - Alias for the developer tools window + Alias for the debug console - `/hmgt status` Prints a compact addon health check - `/hmgt version` - Opens the version window when developer tools are enabled + Opens the version window when the debug console is enabled ## Installation