diff --git a/Heimdall.lua b/Heimdall.lua index aafc097..79fdb39 100644 --- a/Heimdall.lua +++ b/Heimdall.lua @@ -12,6 +12,7 @@ local function init() ---@field config HeimdallConfig ---@field stinkies table if not Heimdall_Data then Heimdall_Data = {} end + ---@class InitTable ---@field Init fun(): nil @@ -37,6 +38,8 @@ local function init() ---@field messenger HeimdallMessengerConfig ---@field deathReporter HeimdallDeathReporterConfig ---@field inviter HeimdallInviterConfig + ---@field dueler HeimdallDuelerConfig + ---@field bully HeimdallBullyConfig ---@field whisperNotify table ---@field stinkies table ---@field agents table @@ -78,6 +81,13 @@ local function init() ---@field allAssist boolean ---@field agentsAssist boolean + ---@class HeimdallDuelerConfig + ---@field enabled boolean + ---@field declineOther boolean + + ---@class HeimdallBullyConfig + ---@field enabled boolean + --- Data --- ---@class HeimdallMessengerData ---@field queue table @@ -163,6 +173,13 @@ local function init() allAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "allAssist" }, false), agentsAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "agentsAssist" }, false), }, + dueler = { + enabled = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "enabled" }, false), + declineOther = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "declineOther" }, false), + }, + bully = { + enabled = shared.GetOrDefault(Heimdall_Data, { "config", "bully", "enabled" }, false), + }, agents = shared.GetOrDefault(Heimdall_Data, { "config", "agents" }, {}), } diff --git a/Heimdall.toc b/Heimdall.toc index 672747c..420e652 100644 --- a/Heimdall.toc +++ b/Heimdall.toc @@ -5,11 +5,13 @@ ## SavedVariables: Heimdall_Data #core -CLEUParser.lua -DumpTable.lua -Spotter.lua -Whoer.lua -Messenger.lua -DeathReporter.lua -Inviter.lua +Modules/CLEUParser.lua +Modules/DumpTable.lua +Modules/Spotter.lua +Modules/Whoer.lua +Modules/Messenger.lua +Modules/DeathReporter.lua +Modules/Inviter.lua +Modules/Dueler.lua +Modules/Bully.lua Heimdall.lua \ No newline at end of file diff --git a/Modules/Bully.lua b/Modules/Bully.lua new file mode 100644 index 0000000..e69de29 diff --git a/CLEUParser.lua b/Modules/CLEUParser.lua similarity index 96% rename from CLEUParser.lua rename to Modules/CLEUParser.lua index c75d79e..cd782f9 100644 --- a/CLEUParser.lua +++ b/Modules/CLEUParser.lua @@ -1,1292 +1,1292 @@ -local function Init() - if CLEUParser then return end - CLEUEventInfo = { - ["GENERIC"] = { - ["timestamp"] = 1, - ["subevent"] = 2, - ["hideCaster"] = 3, - ["sourceGUID"] = 4, - ["sourceName"] = 5, - ["sourceFlags"] = 6, - ["sourceRaidFlags"] = 7, - ["destGUID"] = 8, - ["destName"] = 9, - ["destFlags"] = 10, - ["destRaidFlags"] = 11 - }, - ["GENERIC_SPELL"] = { - ["spellId"] = 12, - ["spellName"] = 13, - ["spellSchool"] = 14 - }, - ["GENERIC_DAMAGE"] = { - ["amount"] = 15, - ["overkill"] = 16, - ["school"] = 17, - ["resisted"] = 18, - ["blocked"] = 19, - ["absorbed"] = 20, - ["critical"] = 21, - ["glancing"] = 22, - ["crushing"] = 23, - ["isOffHand"] = 24 - }, - ["GENERIC_MISSED"] = { - ["missType"] = 15, - ["isOffHand"] = 16, - ["amountMissed"] = 17, - ["critical"] = 18 - }, - ["GENERIC_HEAL"] = { - ["amount"] = 15, - ["overhealing"] = 16, - ["absorbed"] = 17, - ["critical"] = 18 - }, - ["GENERIC_HEAL_ABSORBED"] = { - ["extraGUID"] = 15, - ["extraName"] = 17, - ["extraFlags"] = 17, - ["extraRaidFlags"] = 18, - ["extraSpellID"] = 19, - ["extraSpellName"] = 20, - ["extraSchool"] = 21, - ["absorbedAmount"] = 22, - ["totalAmount"] = 23 - }, - ["GENERIC_ENERGIZE"] = { - ["amount"] = 15, - ["overEnergize"] = 16, - ["powerType"] = 17 - }, - ["GENERIC_DRAIN"] = { - ["amount"] = 15, - ["powerType"] = 16, - ["extraAmount"] = 17 - }, - ["GENERIC_LEECH"] = { - ["amount"] = 15, - ["powerType"] = 16, - ["extraAmount"] = 17 - }, - ["GENERIC_INTERRUPT"] = { - ["extraSpellId"] = 15, - ["extraSpellName"] = 16, - ["extraSchool"] = 17 - }, - ["GENERIC_DISPEL"] = { - ["extraSpellId"] = 15, - ["extraSpellName"] = 16, - ["extraSchool"] = 17, - ["auraType"] = 18 - }, - ["GENERIC_DISPEL_FAILED"] = { - ["extraSpellId"] = 15, - ["extraSpellName"] = 16, - ["extraSchool"] = 17 - }, - ["GENERIC_STOLEN"] = { - ["extraSpellId"] = 15, - ["extraSpellName"] = 16, - ["extraSchool"] = 17, - ["auraType"] = 18 - }, - ["GENERIC_EXTRA_ATTACKS"] = { ["amount"] = 15 }, - ["GENERIC_AURA_APPLIED"] = { ["auraType"] = 15, ["amount"] = 16 }, - ["GENERIC_AURA_REMOVED"] = { ["auraType"] = 15, ["amount"] = 16 }, - ["GENERIC_AURA_APPLIED_DOSE"] = { ["auraType"] = 15, ["amount"] = 16 }, - ["GENERIC_AURA_REMOVED_DOSE"] = { ["auraType"] = 15, ["amount"] = 16 }, - ["GENERIC_AURA_REFRESH"] = { ["auraType"] = 15 }, - ["GENERIC_AURA_BROKEN"] = { ["auraType"] = 15 }, - ["GENERIC_AURA_BROKEN_SPELL"] = { - ["extraSpellId"] = 15, - ["extraSpellName"] = 16, - ["extraSchool"] = 17, - ["auraType"] = 18 - }, - ["GENERIC_CAST_START"] = {}, - ["GENERIC_CAST_SUCCESS"] = {}, - ["GENERIC_CAST_FAILED"] = {} - } - - CLEUEventInfo["SWING_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] - CLEUEventInfo["SWING_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] - CLEUEventInfo["SWING_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] - CLEUEventInfo["SWING_HEAL_ABSORBED"] = - CLEUEventInfo["GENERIC_HEAL_ABSORBED"] - CLEUEventInfo["SWING_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] - CLEUEventInfo["SWING_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] - CLEUEventInfo["SWING_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] - CLEUEventInfo["SWING_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] - CLEUEventInfo["SWING_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] - CLEUEventInfo["SWING_DISPEL_FAILED"] = - CLEUEventInfo["GENERIC_DISPEL_FAILED"] - CLEUEventInfo["SWING_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] - CLEUEventInfo["SWING_EXTRA_ATTACKS"] = - CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] - CLEUEventInfo["SWING_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] - CLEUEventInfo["SWING_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] - CLEUEventInfo["SWING_AURA_APPLIED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] - CLEUEventInfo["SWING_AURA_REMOVED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] - CLEUEventInfo["SWING_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] - CLEUEventInfo["SWING_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] - CLEUEventInfo["SWING_AURA_BROKEN_SPELL"] = - CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] - CLEUEventInfo["SWING_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] - CLEUEventInfo["SWING_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] - CLEUEventInfo["SWING_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] - - CLEUEventInfo["RANGE_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] - CLEUEventInfo["RANGE_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] - CLEUEventInfo["RANGE_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] - CLEUEventInfo["RANGE_HEAL_ABSORBED"] = - CLEUEventInfo["GENERIC_HEAL_ABSORBED"] - CLEUEventInfo["RANGE_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] - CLEUEventInfo["RANGE_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] - CLEUEventInfo["RANGE_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] - CLEUEventInfo["RANGE_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] - CLEUEventInfo["RANGE_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] - CLEUEventInfo["RANGE_DISPEL_FAILED"] = - CLEUEventInfo["GENERIC_DISPEL_FAILED"] - CLEUEventInfo["RANGE_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] - CLEUEventInfo["RANGE_EXTRA_ATTACKS"] = - CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] - CLEUEventInfo["RANGE_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] - CLEUEventInfo["RANGE_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] - CLEUEventInfo["RANGE_AURA_APPLIED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] - CLEUEventInfo["RANGE_AURA_REMOVED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] - CLEUEventInfo["RANGE_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] - CLEUEventInfo["RANGE_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] - CLEUEventInfo["RANGE_AURA_BROKEN_SPELL"] = - CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] - CLEUEventInfo["RANGE_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] - CLEUEventInfo["RANGE_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] - CLEUEventInfo["RANGE_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] - - CLEUEventInfo["SPELL_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] - CLEUEventInfo["SPELL_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] - CLEUEventInfo["SPELL_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] - CLEUEventInfo["SPELL_HEAL_ABSORBED"] = - CLEUEventInfo["GENERIC_HEAL_ABSORBED"] - CLEUEventInfo["SPELL_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] - CLEUEventInfo["SPELL_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] - CLEUEventInfo["SPELL_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] - CLEUEventInfo["SPELL_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] - CLEUEventInfo["SPELL_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] - CLEUEventInfo["SPELL_DISPEL_FAILED"] = - CLEUEventInfo["GENERIC_DISPEL_FAILED"] - CLEUEventInfo["SPELL_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] - CLEUEventInfo["SPELL_EXTRA_ATTACKS"] = - CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] - CLEUEventInfo["SPELL_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] - CLEUEventInfo["SPELL_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] - CLEUEventInfo["SPELL_AURA_APPLIED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] - CLEUEventInfo["SPELL_AURA_REMOVED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] - CLEUEventInfo["SPELL_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] - CLEUEventInfo["SPELL_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] - CLEUEventInfo["SPELL_AURA_BROKEN_SPELL"] = - CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] - CLEUEventInfo["SPELL_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] - CLEUEventInfo["SPELL_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] - CLEUEventInfo["SPELL_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] - - CLEUEventInfo["SPELL_PERIODIC_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] - CLEUEventInfo["SPELL_PERIODIC_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] - CLEUEventInfo["SPELL_PERIODIC_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] - CLEUEventInfo["SPELL_PERIODIC_HEAL_ABSORBED"] = - CLEUEventInfo["GENERIC_HEAL_ABSORBED"] - CLEUEventInfo["SPELL_PERIODIC_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] - CLEUEventInfo["SPELL_PERIODIC_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] - CLEUEventInfo["SPELL_PERIODIC_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] - CLEUEventInfo["SPELL_PERIODIC_INTERRUPT"] = - CLEUEventInfo["GENERIC_INTERRUPT"] - CLEUEventInfo["SPELL_PERIODIC_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] - CLEUEventInfo["SPELL_PERIODIC_DISPEL_FAILED"] = - CLEUEventInfo["GENERIC_DISPEL_FAILED"] - CLEUEventInfo["SPELL_PERIODIC_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] - CLEUEventInfo["SPELL_PERIODIC_EXTRA_ATTACKS"] = - CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] - CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED"] = - CLEUEventInfo["GENERIC_AURA_APPLIED"] - CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED"] = - CLEUEventInfo["GENERIC_AURA_REMOVED"] - CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] - CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED_DOSE"] = - CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] - CLEUEventInfo["SPELL_PERIODIC_AURA_REFRESH"] = - CLEUEventInfo["GENERIC_AURA_REFRESH"] - CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN"] = - CLEUEventInfo["GENERIC_AURA_BROKEN"] - CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN_SPELL"] = - CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] - CLEUEventInfo["SPELL_PERIODIC_CAST_START"] = - CLEUEventInfo["GENERIC_CAST_START"] - CLEUEventInfo["SPELL_PERIODIC_CAST_SUCCESS"] = - CLEUEventInfo["GENERIC_CAST_SUCCESS"] - CLEUEventInfo["SPELL_PERIODIC_CAST_FAILED"] = - CLEUEventInfo["GENERIC_CAST_FAILED"] - - ---@class CLEUParser - CLEUParser = { - ---@param ... any - ---@return number, nil|string - GetTimestamp = function(...) - local val = select(CLEUEventInfo["GENERIC"]["timestamp"], ...) - if val == nil then - return 0, "Timestamp is nil or missing" - end - if type(val) ~= "number" then - return 0, "Timestamp is not a number" - end - return val, nil - end, - ---@param ... any - ---@return string, nil|string - GetSubevent = function(...) - local val = select(CLEUEventInfo["GENERIC"]["subevent"], ...) - if val == nil then - return "", "Subevent is nil or missing" - end - if type(val) ~= "string" then - return "", "Subevent is not a string" - end - return val, nil - end, - ---@param ... any - ---@return boolean, nil|string - GetHideCaster = function(...) - local val = select(CLEUEventInfo["GENERIC"]["hideCaster"], ...) - if val == nil then - return false, "HideCaster is nil or missing" - end - if type(val) ~= "boolean" then - return false, "HideCaster is not a boolean" - end - return val, nil - end, - ---@param ... any - ---@return string, nil|string - GetSourceGUID = function(...) - local val = select(CLEUEventInfo["GENERIC"]["sourceGUID"], ...) - if val == nil then - return "", "SourceGUID is nil or missing" - end - if type(val) ~= "string" then - return "", "SourceGUID is not a string" - end - return val, nil - end, - ---@param ... any - ---@return string, nil|string - GetSourceName = function(...) - local val = select(CLEUEventInfo["GENERIC"]["sourceName"], ...) - if val == nil then - return "", "SourceName is nil or missing" - end - if type(val) ~= "string" then - return "", "SourceName is not a string" - end - return val, nil - end, - ---@param ... any - ---@return number, nil|string - GetSourceFlags = function(...) - local val = select(CLEUEventInfo["GENERIC"]["sourceFlags"], ...) - if val == nil then - return 0, "SourceFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "SourceFlags is not a number" - end - return val, nil - end, - ---@param ... any - ---@return number, nil|string - GetSourceRaidFlags = function(...) - local val = select(CLEUEventInfo["GENERIC"]["sourceRaidFlags"], ...) - if val == nil then - return 0, "SourceRaidFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "SourceRaidFlags is not a number" - end - return val, nil - end, - ---@param ... any - ---@return string, nil|string - GetDestGUID = function(...) - local val = select(CLEUEventInfo["GENERIC"]["destGUID"], ...) - if val == nil then - return "", "DestGUID is nil or missing" - end - if type(val) ~= "string" then - return "", "DestGUID is not a string" - end - return val, nil - end, - ---@param ... any - ---@return string, nil|string - GetDestName = function(...) - local val = select(CLEUEventInfo["GENERIC"]["destName"], ...) - if val == nil then - return "", "DestName is nil or missing" - end - if type(val) ~= "string" then - return "", "DestName is not a string" - end - return val, nil - end, - ---@param ... any - ---@return number, nil|string - GetDestFlags = function(...) - local val = select(CLEUEventInfo["GENERIC"]["destFlags"], ...) - if val == nil then - return 0, "DestFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "DestFlags is not a number" - end - return val, nil - end, - ---@param ... any - ---@return number, nil|string - GetDestRaidFlags = function(...) - local val = select(CLEUEventInfo["GENERIC"]["destRaidFlags"], ...) - if val == nil then - return 0, "DestRaidFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "DestRaidFlags is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - ---@param ... any - ---@return number, nil|string - GetSpellId = function(...) - local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellId"], ...) - if val == nil then return 0, "SpellId is nil or missing" end - if type(val) ~= "number" then - return 0, "SpellId is not a number" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - ---@param ... any - ---@return string, nil|string - GetSpellName = function(...) - local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellName"], ...) - if val == nil then - return "", "SpellName is nil or missing" - end - if type(val) ~= "string" then - return "", "SpellName is not a string" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - ---@param ... any - ---@return number, nil|string - GetSpellSchool = function(...) - local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellSchool"], - ...) - if val == nil then - return 0, "SpellSchool is nil or missing" - end - if type(val) ~= "number" then - return 0, "SpellSchool is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- |_HEAL| - --- |_ENERGIZE| - --- |_DRAIN| - --- |_LEECH| - --- |_EXTRA_ATTACKS| - --- |_AURA_APPLIED| - --- |_AURA_REMOVED| - --- |_AURA_APPLIED_DOSE| - --- |_AURA_REMOVED_DOSE| - ---@param ... any - ---@return number, nil|string - GetAmount = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["amount"], ...) - if val == nil then return 0, "Amount is nil or missing" end - if type(val) ~= "number" then - return 0, "Amount is not a number" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - ---@param ... any - ---@return number, nil|string - GetOverkill = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - if not CLEUEventInfo[subevent] then - return 0, "Subevent is not a valid event" - end - if not CLEUEventInfo[subevent]["overkill"] then - return 0, "Overkill is nil or missing" - end - local val = select(CLEUEventInfo[subevent]["overkill"], ...) - if val == nil then return 0, "Overkill is nil or missing" end - if type(val) ~= "number" then - return 0, "Overkill is not a number" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - ---@param ... any - ---@return number, nil|string - GetSchool = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["school"], ...) - if val == nil then return 0, "School is nil or missing" end - if type(val) ~= "number" then - return 0, "School is not a number" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- - --- Apparently this is allowed to be nil? - ---@param ... any - ---@return boolean, nil|string - GetResisted = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["resisted"], ...) - if val == nil then - return false, "Resisted is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Resisted is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- - --- Apparently this is allowed to be nil? - ---@param ... any - ---@return boolean, nil|string - GetBlocked = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["blocked"], ...) - if val == nil then - return false, "Blocked is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Blocked is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- |_HEAL| - --- - --- Apparently this is allowed to be nil? - ---@param ... any - ---@return boolean, nil|string - GetAbsorbed = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["absorbed"], ...) - if val == nil then - return false, "Absorbed is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Absorbed is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- |_MISSED| - --- |_HEAL| - ---@param ... any - ---@return boolean, nil|string - GetCritical = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["critical"], ...) - if val == nil then - return false, "Critical is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Critical is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - ---@param ... any - ---@return boolean, nil|string - GetGlancing = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["glancing"], ...) - if val == nil then - return false, "Glancing is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Glancing is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - ---@param ... any - ---@return boolean, nil|string - GetCrushing = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["crushing"], ...) - if val == nil then - return false, "Crushing is nil or missing" - end - if type(val) ~= "boolean" then - return false, "Crushing is not a boolean" - end - return val, nil - end, - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DAMAGE| - --- |_MISSED| - ---@param ... any - ---@return boolean, nil|string - GetIsOffHand = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return false, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["isOffHand"], ...) - if val == nil then - return false, "IsOffHand is nil or missing" - end - if type(val) ~= "boolean" then - return false, "IsOffHand is not a boolean" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_MISSED| - --- - --- return type is unconfirmed! - ---@param ... any - ---@return string, nil|string - GetMissType = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return "", - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["missType"], ...) - if val == nil then - return "", "MissType is nil or missing" - end - if type(val) ~= "string" then - return "", "MissType is not a string" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_MISSED| - ---@param ... any - ---@return number, nil|string - --- - --- return type is unconfirmed! - GetAmountMissed = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["amountMissed"], ...) - if val == nil then - return 0, "AmountMissed is nil or missing" - end - if type(val) ~= "number" then - return 0, "AmountMissed is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL| - --- - --- return type is unconfirmed! - ---@param ... any - ---@return number, nil|string - GetOverhealing = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["overhealing"], ...) - if val == nil then - return 0, "Overhealing is nil or missing" - end - if type(val) ~= "number" then - return 0, "Overhealing is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - ---@param ... any - ---@return string, nil|string - GetExtraGUID = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return "", - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraGUID"], ...) - if val == nil then - return "", "ExtraGUID is nil or missing" - end - if type(val) ~= "string" then - return "", "ExtraGUID is not a string" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - ---@param ... any - ---@return string, nil|string - GetExtraName = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return "", - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraName"], ...) - if val == nil then - return "", "ExtraName is nil or missing" - end - if type(val) ~= "string" then - return "", "ExtraName is not a string" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - ---@param ... any - ---@return number, nil|string - GetExtraFlags = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraFlags"], ...) - if val == nil then - return 0, "ExtraFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraFlags is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - ---@param ... any - ---@return number, nil|string - GetExtraRaidFlags = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraRaidFlags"], ...) - if val == nil then - return 0, "ExtraRaidFlags is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraRaidFlags is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - --- - --- WARNING! Different from "GetExtraSpellId" (capital ID!)
- --- This is the doing of Blizzard, not me.
- ---@see CLEUParser.GetExtraSpellId - ---@param ... any - ---@return number, nil|string - GetExtraSpellID = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraSpellID"], ...) - if val == nil then - return 0, "ExtraSpellID is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraSpellID is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - --- |_INTERRUPT| - --- |_DISPEL| - --- |_DISPEL_FAILED| - --- |_STOLEN| - --- |_AURA_BROKEN_SPELL| - ---@param ... any - ---@return string, nil|string - GetExtraSpellName = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return "", - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraSpellName"], ...) - if val == nil then - return "", "extraSpellName is nil or missing" - end - if type(val) ~= "string" then - return "", "extraSpellName is not a string" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - --- |_INTERRUPT| - --- |_DISPEL| - --- |_DISPEL_FAILED| - --- |_STOLEN| - --- |_AURA_BROKEN_SPELL| - ---@param ... any - ---@return number, nil|string - GetExtraSchool = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraSchool"], ...) - if val == nil then - return 0, "ExtraSchool is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraSchool is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_HEAL_ABSORBED| - ---@param ... any - ---@return number, nil|string - GetAbsorbedAmount = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["absorbedAmount"], ...) - if val == nil then - return 0, "AbsorbedAmount is nil or missing" - end - if type(val) ~= "number" then - return 0, "AbsorbedAmount is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_ENERGIZE| - ---@param ... any - ---@return number, nil|string - GetOverEnergize = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["overEnergize"], ...) - if val == nil then - return 0, "OverEnergize is nil or missing" - end - if type(val) ~= "number" then - return 0, "OverEnergize is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_ENERGIZE| - --- |_DRAIN| - --- |_LEECH| - --- - --- return type is unconfirmed! - ---@param ... any - ---@return number, nil|string - GetPowerType = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["powerType"], ...) - if val == nil then - return 0, "PowerType is nil or missing" - end - if type(val) ~= "number" then - return 0, "PowerType is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DRAIN| - --- |_LEECH| - ---@param ... any - ---@return number, nil|string - GetExtraAmount = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraAmount"], ...) - if val == nil then - return 0, "ExtraAmount is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraAmount is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_INTERRUPT| - --- |_DISPEL| - --- |_DISPEL_FAILED| - --- |_STOLEN| - --- |_AURA_BROKEN_SPELL| - --- - --- WARNING! Different from "GetExtraSpellID" (capital ID!)
- --- This is the doing of Blizzard, not me.
- ---@see CLEUParser.GetExtraSpellID - ---@param ... any - ---@return number, nil|string - GetExtraSpellId = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["extraSpellId"], ...) - if val == nil then - return 0, "ExtraSpellId is nil or missing" - end - if type(val) ~= "number" then - return 0, "ExtraSpellId is not a number" - end - return val, nil - end, - - --- Specific to subevents prefixed by: - --- |Prefix| - --- |-----| - --- |RANGE| - --- |SWING| - --- |SPELL| - --- |SPELL_PERIODIC| - --- |SPELL_BUILDING| - --- - --- And suffixed by: - --- |Suffix| - --- |------| - --- |_DISPEL| - --- |_STOLEN| - --- |_AURA_APPLIED| - --- |_AURA_REMOVED| - --- |_AURA_APPLIED_DOSE| - --- |_AURA_REMOVED_DOSE| - --- |_AURA_REFRESH| - --- |_AURA_BROKEN| - --- |_AURA_BROKEN_SPELL| - ---@param ... any - ---@return number, nil|string - GetExtraAuraType = function(...) - local subevent, err = CLEUParser.GetSubevent(...) - if err then - return 0, - string.format("Failed getting subevent due to: %s", err) - end - local val = select(CLEUEventInfo[subevent]["auraType"], ...) - if val == nil then return 0, "AuraType is nil or missing" end - if type(val) ~= "number" then - return 0, "AuraType is not a number" - end - return val, nil - end - } -end - -local frame = CreateFrame("Frame") -frame:RegisterEvent("PLAYER_LOGIN") -frame:RegisterEvent("PLAYER_ENTERING_WORLD") -frame:RegisterEvent("GUILD_ROSTER_UPDATE") -frame:SetScript("OnEvent", function(self, event, ...) - Init() -end) +local function Init() + if CLEUParser then return end + CLEUEventInfo = { + ["GENERIC"] = { + ["timestamp"] = 1, + ["subevent"] = 2, + ["hideCaster"] = 3, + ["sourceGUID"] = 4, + ["sourceName"] = 5, + ["sourceFlags"] = 6, + ["sourceRaidFlags"] = 7, + ["destGUID"] = 8, + ["destName"] = 9, + ["destFlags"] = 10, + ["destRaidFlags"] = 11 + }, + ["GENERIC_SPELL"] = { + ["spellId"] = 12, + ["spellName"] = 13, + ["spellSchool"] = 14 + }, + ["GENERIC_DAMAGE"] = { + ["amount"] = 15, + ["overkill"] = 16, + ["school"] = 17, + ["resisted"] = 18, + ["blocked"] = 19, + ["absorbed"] = 20, + ["critical"] = 21, + ["glancing"] = 22, + ["crushing"] = 23, + ["isOffHand"] = 24 + }, + ["GENERIC_MISSED"] = { + ["missType"] = 15, + ["isOffHand"] = 16, + ["amountMissed"] = 17, + ["critical"] = 18 + }, + ["GENERIC_HEAL"] = { + ["amount"] = 15, + ["overhealing"] = 16, + ["absorbed"] = 17, + ["critical"] = 18 + }, + ["GENERIC_HEAL_ABSORBED"] = { + ["extraGUID"] = 15, + ["extraName"] = 17, + ["extraFlags"] = 17, + ["extraRaidFlags"] = 18, + ["extraSpellID"] = 19, + ["extraSpellName"] = 20, + ["extraSchool"] = 21, + ["absorbedAmount"] = 22, + ["totalAmount"] = 23 + }, + ["GENERIC_ENERGIZE"] = { + ["amount"] = 15, + ["overEnergize"] = 16, + ["powerType"] = 17 + }, + ["GENERIC_DRAIN"] = { + ["amount"] = 15, + ["powerType"] = 16, + ["extraAmount"] = 17 + }, + ["GENERIC_LEECH"] = { + ["amount"] = 15, + ["powerType"] = 16, + ["extraAmount"] = 17 + }, + ["GENERIC_INTERRUPT"] = { + ["extraSpellId"] = 15, + ["extraSpellName"] = 16, + ["extraSchool"] = 17 + }, + ["GENERIC_DISPEL"] = { + ["extraSpellId"] = 15, + ["extraSpellName"] = 16, + ["extraSchool"] = 17, + ["auraType"] = 18 + }, + ["GENERIC_DISPEL_FAILED"] = { + ["extraSpellId"] = 15, + ["extraSpellName"] = 16, + ["extraSchool"] = 17 + }, + ["GENERIC_STOLEN"] = { + ["extraSpellId"] = 15, + ["extraSpellName"] = 16, + ["extraSchool"] = 17, + ["auraType"] = 18 + }, + ["GENERIC_EXTRA_ATTACKS"] = { ["amount"] = 15 }, + ["GENERIC_AURA_APPLIED"] = { ["auraType"] = 15, ["amount"] = 16 }, + ["GENERIC_AURA_REMOVED"] = { ["auraType"] = 15, ["amount"] = 16 }, + ["GENERIC_AURA_APPLIED_DOSE"] = { ["auraType"] = 15, ["amount"] = 16 }, + ["GENERIC_AURA_REMOVED_DOSE"] = { ["auraType"] = 15, ["amount"] = 16 }, + ["GENERIC_AURA_REFRESH"] = { ["auraType"] = 15 }, + ["GENERIC_AURA_BROKEN"] = { ["auraType"] = 15 }, + ["GENERIC_AURA_BROKEN_SPELL"] = { + ["extraSpellId"] = 15, + ["extraSpellName"] = 16, + ["extraSchool"] = 17, + ["auraType"] = 18 + }, + ["GENERIC_CAST_START"] = {}, + ["GENERIC_CAST_SUCCESS"] = {}, + ["GENERIC_CAST_FAILED"] = {} + } + + CLEUEventInfo["SWING_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] + CLEUEventInfo["SWING_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] + CLEUEventInfo["SWING_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] + CLEUEventInfo["SWING_HEAL_ABSORBED"] = + CLEUEventInfo["GENERIC_HEAL_ABSORBED"] + CLEUEventInfo["SWING_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] + CLEUEventInfo["SWING_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] + CLEUEventInfo["SWING_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] + CLEUEventInfo["SWING_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] + CLEUEventInfo["SWING_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] + CLEUEventInfo["SWING_DISPEL_FAILED"] = + CLEUEventInfo["GENERIC_DISPEL_FAILED"] + CLEUEventInfo["SWING_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] + CLEUEventInfo["SWING_EXTRA_ATTACKS"] = + CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] + CLEUEventInfo["SWING_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] + CLEUEventInfo["SWING_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] + CLEUEventInfo["SWING_AURA_APPLIED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] + CLEUEventInfo["SWING_AURA_REMOVED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] + CLEUEventInfo["SWING_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] + CLEUEventInfo["SWING_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] + CLEUEventInfo["SWING_AURA_BROKEN_SPELL"] = + CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] + CLEUEventInfo["SWING_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] + CLEUEventInfo["SWING_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] + CLEUEventInfo["SWING_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] + + CLEUEventInfo["RANGE_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] + CLEUEventInfo["RANGE_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] + CLEUEventInfo["RANGE_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] + CLEUEventInfo["RANGE_HEAL_ABSORBED"] = + CLEUEventInfo["GENERIC_HEAL_ABSORBED"] + CLEUEventInfo["RANGE_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] + CLEUEventInfo["RANGE_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] + CLEUEventInfo["RANGE_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] + CLEUEventInfo["RANGE_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] + CLEUEventInfo["RANGE_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] + CLEUEventInfo["RANGE_DISPEL_FAILED"] = + CLEUEventInfo["GENERIC_DISPEL_FAILED"] + CLEUEventInfo["RANGE_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] + CLEUEventInfo["RANGE_EXTRA_ATTACKS"] = + CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] + CLEUEventInfo["RANGE_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] + CLEUEventInfo["RANGE_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] + CLEUEventInfo["RANGE_AURA_APPLIED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] + CLEUEventInfo["RANGE_AURA_REMOVED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] + CLEUEventInfo["RANGE_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] + CLEUEventInfo["RANGE_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] + CLEUEventInfo["RANGE_AURA_BROKEN_SPELL"] = + CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] + CLEUEventInfo["RANGE_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] + CLEUEventInfo["RANGE_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] + CLEUEventInfo["RANGE_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] + + CLEUEventInfo["SPELL_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] + CLEUEventInfo["SPELL_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] + CLEUEventInfo["SPELL_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] + CLEUEventInfo["SPELL_HEAL_ABSORBED"] = + CLEUEventInfo["GENERIC_HEAL_ABSORBED"] + CLEUEventInfo["SPELL_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] + CLEUEventInfo["SPELL_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] + CLEUEventInfo["SPELL_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] + CLEUEventInfo["SPELL_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] + CLEUEventInfo["SPELL_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] + CLEUEventInfo["SPELL_DISPEL_FAILED"] = + CLEUEventInfo["GENERIC_DISPEL_FAILED"] + CLEUEventInfo["SPELL_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] + CLEUEventInfo["SPELL_EXTRA_ATTACKS"] = + CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] + CLEUEventInfo["SPELL_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] + CLEUEventInfo["SPELL_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] + CLEUEventInfo["SPELL_AURA_APPLIED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] + CLEUEventInfo["SPELL_AURA_REMOVED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] + CLEUEventInfo["SPELL_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] + CLEUEventInfo["SPELL_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] + CLEUEventInfo["SPELL_AURA_BROKEN_SPELL"] = + CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] + CLEUEventInfo["SPELL_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] + CLEUEventInfo["SPELL_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] + CLEUEventInfo["SPELL_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] + + CLEUEventInfo["SPELL_PERIODIC_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] + CLEUEventInfo["SPELL_PERIODIC_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] + CLEUEventInfo["SPELL_PERIODIC_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] + CLEUEventInfo["SPELL_PERIODIC_HEAL_ABSORBED"] = + CLEUEventInfo["GENERIC_HEAL_ABSORBED"] + CLEUEventInfo["SPELL_PERIODIC_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] + CLEUEventInfo["SPELL_PERIODIC_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] + CLEUEventInfo["SPELL_PERIODIC_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] + CLEUEventInfo["SPELL_PERIODIC_INTERRUPT"] = + CLEUEventInfo["GENERIC_INTERRUPT"] + CLEUEventInfo["SPELL_PERIODIC_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] + CLEUEventInfo["SPELL_PERIODIC_DISPEL_FAILED"] = + CLEUEventInfo["GENERIC_DISPEL_FAILED"] + CLEUEventInfo["SPELL_PERIODIC_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] + CLEUEventInfo["SPELL_PERIODIC_EXTRA_ATTACKS"] = + CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] + CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED"] = + CLEUEventInfo["GENERIC_AURA_APPLIED"] + CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED"] = + CLEUEventInfo["GENERIC_AURA_REMOVED"] + CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] + CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED_DOSE"] = + CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] + CLEUEventInfo["SPELL_PERIODIC_AURA_REFRESH"] = + CLEUEventInfo["GENERIC_AURA_REFRESH"] + CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN"] = + CLEUEventInfo["GENERIC_AURA_BROKEN"] + CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN_SPELL"] = + CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"] + CLEUEventInfo["SPELL_PERIODIC_CAST_START"] = + CLEUEventInfo["GENERIC_CAST_START"] + CLEUEventInfo["SPELL_PERIODIC_CAST_SUCCESS"] = + CLEUEventInfo["GENERIC_CAST_SUCCESS"] + CLEUEventInfo["SPELL_PERIODIC_CAST_FAILED"] = + CLEUEventInfo["GENERIC_CAST_FAILED"] + + ---@class CLEUParser + CLEUParser = { + ---@param ... any + ---@return number, nil|string + GetTimestamp = function(...) + local val = select(CLEUEventInfo["GENERIC"]["timestamp"], ...) + if val == nil then + return 0, "Timestamp is nil or missing" + end + if type(val) ~= "number" then + return 0, "Timestamp is not a number" + end + return val, nil + end, + ---@param ... any + ---@return string, nil|string + GetSubevent = function(...) + local val = select(CLEUEventInfo["GENERIC"]["subevent"], ...) + if val == nil then + return "", "Subevent is nil or missing" + end + if type(val) ~= "string" then + return "", "Subevent is not a string" + end + return val, nil + end, + ---@param ... any + ---@return boolean, nil|string + GetHideCaster = function(...) + local val = select(CLEUEventInfo["GENERIC"]["hideCaster"], ...) + if val == nil then + return false, "HideCaster is nil or missing" + end + if type(val) ~= "boolean" then + return false, "HideCaster is not a boolean" + end + return val, nil + end, + ---@param ... any + ---@return string, nil|string + GetSourceGUID = function(...) + local val = select(CLEUEventInfo["GENERIC"]["sourceGUID"], ...) + if val == nil then + return "", "SourceGUID is nil or missing" + end + if type(val) ~= "string" then + return "", "SourceGUID is not a string" + end + return val, nil + end, + ---@param ... any + ---@return string, nil|string + GetSourceName = function(...) + local val = select(CLEUEventInfo["GENERIC"]["sourceName"], ...) + if val == nil then + return "", "SourceName is nil or missing" + end + if type(val) ~= "string" then + return "", "SourceName is not a string" + end + return val, nil + end, + ---@param ... any + ---@return number, nil|string + GetSourceFlags = function(...) + local val = select(CLEUEventInfo["GENERIC"]["sourceFlags"], ...) + if val == nil then + return 0, "SourceFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "SourceFlags is not a number" + end + return val, nil + end, + ---@param ... any + ---@return number, nil|string + GetSourceRaidFlags = function(...) + local val = select(CLEUEventInfo["GENERIC"]["sourceRaidFlags"], ...) + if val == nil then + return 0, "SourceRaidFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "SourceRaidFlags is not a number" + end + return val, nil + end, + ---@param ... any + ---@return string, nil|string + GetDestGUID = function(...) + local val = select(CLEUEventInfo["GENERIC"]["destGUID"], ...) + if val == nil then + return "", "DestGUID is nil or missing" + end + if type(val) ~= "string" then + return "", "DestGUID is not a string" + end + return val, nil + end, + ---@param ... any + ---@return string, nil|string + GetDestName = function(...) + local val = select(CLEUEventInfo["GENERIC"]["destName"], ...) + if val == nil then + return "", "DestName is nil or missing" + end + if type(val) ~= "string" then + return "", "DestName is not a string" + end + return val, nil + end, + ---@param ... any + ---@return number, nil|string + GetDestFlags = function(...) + local val = select(CLEUEventInfo["GENERIC"]["destFlags"], ...) + if val == nil then + return 0, "DestFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "DestFlags is not a number" + end + return val, nil + end, + ---@param ... any + ---@return number, nil|string + GetDestRaidFlags = function(...) + local val = select(CLEUEventInfo["GENERIC"]["destRaidFlags"], ...) + if val == nil then + return 0, "DestRaidFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "DestRaidFlags is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + ---@param ... any + ---@return number, nil|string + GetSpellId = function(...) + local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellId"], ...) + if val == nil then return 0, "SpellId is nil or missing" end + if type(val) ~= "number" then + return 0, "SpellId is not a number" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + ---@param ... any + ---@return string, nil|string + GetSpellName = function(...) + local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellName"], ...) + if val == nil then + return "", "SpellName is nil or missing" + end + if type(val) ~= "string" then + return "", "SpellName is not a string" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + ---@param ... any + ---@return number, nil|string + GetSpellSchool = function(...) + local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellSchool"], + ...) + if val == nil then + return 0, "SpellSchool is nil or missing" + end + if type(val) ~= "number" then + return 0, "SpellSchool is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- |_HEAL| + --- |_ENERGIZE| + --- |_DRAIN| + --- |_LEECH| + --- |_EXTRA_ATTACKS| + --- |_AURA_APPLIED| + --- |_AURA_REMOVED| + --- |_AURA_APPLIED_DOSE| + --- |_AURA_REMOVED_DOSE| + ---@param ... any + ---@return number, nil|string + GetAmount = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["amount"], ...) + if val == nil then return 0, "Amount is nil or missing" end + if type(val) ~= "number" then + return 0, "Amount is not a number" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + ---@param ... any + ---@return number, nil|string + GetOverkill = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + if not CLEUEventInfo[subevent] then + return 0, "Subevent is not a valid event" + end + if not CLEUEventInfo[subevent]["overkill"] then + return 0, "Overkill is nil or missing" + end + local val = select(CLEUEventInfo[subevent]["overkill"], ...) + if val == nil then return 0, "Overkill is nil or missing" end + if type(val) ~= "number" then + return 0, "Overkill is not a number" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + ---@param ... any + ---@return number, nil|string + GetSchool = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["school"], ...) + if val == nil then return 0, "School is nil or missing" end + if type(val) ~= "number" then + return 0, "School is not a number" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- + --- Apparently this is allowed to be nil? + ---@param ... any + ---@return boolean, nil|string + GetResisted = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["resisted"], ...) + if val == nil then + return false, "Resisted is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Resisted is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- + --- Apparently this is allowed to be nil? + ---@param ... any + ---@return boolean, nil|string + GetBlocked = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["blocked"], ...) + if val == nil then + return false, "Blocked is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Blocked is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- |_HEAL| + --- + --- Apparently this is allowed to be nil? + ---@param ... any + ---@return boolean, nil|string + GetAbsorbed = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["absorbed"], ...) + if val == nil then + return false, "Absorbed is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Absorbed is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- |_MISSED| + --- |_HEAL| + ---@param ... any + ---@return boolean, nil|string + GetCritical = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["critical"], ...) + if val == nil then + return false, "Critical is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Critical is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + ---@param ... any + ---@return boolean, nil|string + GetGlancing = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["glancing"], ...) + if val == nil then + return false, "Glancing is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Glancing is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + ---@param ... any + ---@return boolean, nil|string + GetCrushing = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["crushing"], ...) + if val == nil then + return false, "Crushing is nil or missing" + end + if type(val) ~= "boolean" then + return false, "Crushing is not a boolean" + end + return val, nil + end, + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DAMAGE| + --- |_MISSED| + ---@param ... any + ---@return boolean, nil|string + GetIsOffHand = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return false, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["isOffHand"], ...) + if val == nil then + return false, "IsOffHand is nil or missing" + end + if type(val) ~= "boolean" then + return false, "IsOffHand is not a boolean" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_MISSED| + --- + --- return type is unconfirmed! + ---@param ... any + ---@return string, nil|string + GetMissType = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return "", + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["missType"], ...) + if val == nil then + return "", "MissType is nil or missing" + end + if type(val) ~= "string" then + return "", "MissType is not a string" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_MISSED| + ---@param ... any + ---@return number, nil|string + --- + --- return type is unconfirmed! + GetAmountMissed = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["amountMissed"], ...) + if val == nil then + return 0, "AmountMissed is nil or missing" + end + if type(val) ~= "number" then + return 0, "AmountMissed is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL| + --- + --- return type is unconfirmed! + ---@param ... any + ---@return number, nil|string + GetOverhealing = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["overhealing"], ...) + if val == nil then + return 0, "Overhealing is nil or missing" + end + if type(val) ~= "number" then + return 0, "Overhealing is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + ---@param ... any + ---@return string, nil|string + GetExtraGUID = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return "", + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraGUID"], ...) + if val == nil then + return "", "ExtraGUID is nil or missing" + end + if type(val) ~= "string" then + return "", "ExtraGUID is not a string" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + ---@param ... any + ---@return string, nil|string + GetExtraName = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return "", + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraName"], ...) + if val == nil then + return "", "ExtraName is nil or missing" + end + if type(val) ~= "string" then + return "", "ExtraName is not a string" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + ---@param ... any + ---@return number, nil|string + GetExtraFlags = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraFlags"], ...) + if val == nil then + return 0, "ExtraFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraFlags is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + ---@param ... any + ---@return number, nil|string + GetExtraRaidFlags = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraRaidFlags"], ...) + if val == nil then + return 0, "ExtraRaidFlags is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraRaidFlags is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + --- + --- WARNING! Different from "GetExtraSpellId" (capital ID!)
+ --- This is the doing of Blizzard, not me.
+ ---@see CLEUParser.GetExtraSpellId + ---@param ... any + ---@return number, nil|string + GetExtraSpellID = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraSpellID"], ...) + if val == nil then + return 0, "ExtraSpellID is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraSpellID is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + --- |_INTERRUPT| + --- |_DISPEL| + --- |_DISPEL_FAILED| + --- |_STOLEN| + --- |_AURA_BROKEN_SPELL| + ---@param ... any + ---@return string, nil|string + GetExtraSpellName = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return "", + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraSpellName"], ...) + if val == nil then + return "", "extraSpellName is nil or missing" + end + if type(val) ~= "string" then + return "", "extraSpellName is not a string" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + --- |_INTERRUPT| + --- |_DISPEL| + --- |_DISPEL_FAILED| + --- |_STOLEN| + --- |_AURA_BROKEN_SPELL| + ---@param ... any + ---@return number, nil|string + GetExtraSchool = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraSchool"], ...) + if val == nil then + return 0, "ExtraSchool is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraSchool is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_HEAL_ABSORBED| + ---@param ... any + ---@return number, nil|string + GetAbsorbedAmount = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["absorbedAmount"], ...) + if val == nil then + return 0, "AbsorbedAmount is nil or missing" + end + if type(val) ~= "number" then + return 0, "AbsorbedAmount is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_ENERGIZE| + ---@param ... any + ---@return number, nil|string + GetOverEnergize = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["overEnergize"], ...) + if val == nil then + return 0, "OverEnergize is nil or missing" + end + if type(val) ~= "number" then + return 0, "OverEnergize is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_ENERGIZE| + --- |_DRAIN| + --- |_LEECH| + --- + --- return type is unconfirmed! + ---@param ... any + ---@return number, nil|string + GetPowerType = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["powerType"], ...) + if val == nil then + return 0, "PowerType is nil or missing" + end + if type(val) ~= "number" then + return 0, "PowerType is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DRAIN| + --- |_LEECH| + ---@param ... any + ---@return number, nil|string + GetExtraAmount = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraAmount"], ...) + if val == nil then + return 0, "ExtraAmount is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraAmount is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_INTERRUPT| + --- |_DISPEL| + --- |_DISPEL_FAILED| + --- |_STOLEN| + --- |_AURA_BROKEN_SPELL| + --- + --- WARNING! Different from "GetExtraSpellID" (capital ID!)
+ --- This is the doing of Blizzard, not me.
+ ---@see CLEUParser.GetExtraSpellID + ---@param ... any + ---@return number, nil|string + GetExtraSpellId = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["extraSpellId"], ...) + if val == nil then + return 0, "ExtraSpellId is nil or missing" + end + if type(val) ~= "number" then + return 0, "ExtraSpellId is not a number" + end + return val, nil + end, + + --- Specific to subevents prefixed by: + --- |Prefix| + --- |-----| + --- |RANGE| + --- |SWING| + --- |SPELL| + --- |SPELL_PERIODIC| + --- |SPELL_BUILDING| + --- + --- And suffixed by: + --- |Suffix| + --- |------| + --- |_DISPEL| + --- |_STOLEN| + --- |_AURA_APPLIED| + --- |_AURA_REMOVED| + --- |_AURA_APPLIED_DOSE| + --- |_AURA_REMOVED_DOSE| + --- |_AURA_REFRESH| + --- |_AURA_BROKEN| + --- |_AURA_BROKEN_SPELL| + ---@param ... any + ---@return number, nil|string + GetExtraAuraType = function(...) + local subevent, err = CLEUParser.GetSubevent(...) + if err then + return 0, + string.format("Failed getting subevent due to: %s", err) + end + local val = select(CLEUEventInfo[subevent]["auraType"], ...) + if val == nil then return 0, "AuraType is nil or missing" end + if type(val) ~= "number" then + return 0, "AuraType is not a number" + end + return val, nil + end + } +end + +local frame = CreateFrame("Frame") +frame:RegisterEvent("PLAYER_LOGIN") +frame:RegisterEvent("PLAYER_ENTERING_WORLD") +frame:RegisterEvent("GUILD_ROSTER_UPDATE") +frame:SetScript("OnEvent", function(self, event, ...) + Init() +end) Init() \ No newline at end of file diff --git a/DeathReporter.lua b/Modules/DeathReporter.lua similarity index 97% rename from DeathReporter.lua rename to Modules/DeathReporter.lua index c013d2d..ada0457 100644 --- a/DeathReporter.lua +++ b/Modules/DeathReporter.lua @@ -1,118 +1,118 @@ -local addonname, shared = ... ----@cast shared HeimdallShared ----@cast addonname string - ----@diagnostic disable-next-line: missing-fields -shared.DeathReporter = {} -function shared.DeathReporter.Init() --- if not Heimdall_Data.config.deathReporter.enabled then --- print("Heimdall - DeathReporter disabled") --- return --- end - - ---@type table - local recentDeaths = {} - ---@type table - local recentDuels = {} - - ---@param source string - ---@param destination string - ---@param spellName string - local function RegisterDeath(source, destination, spellName) - if not Heimdall_Data.config.deathReporter.enabled then return end - if recentDeaths[destination] - and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle then - return - end - - if recentDuels[destination] - and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then - print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) - return - end - if recentDuels[source] - and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then - print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) - return - end - - recentDeaths[destination] = GetTime() - C_Timer.NewTimer(3, function() - if recentDuels[destination] - and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then - print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) - return - end - if recentDuels[source] - and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then - print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) - return - end - - local zone = Heimdall_Data.config.deathReporter.zoneOverride - if not zone then - zone = string.format("%s (%s)", GetZoneText(), GetSubZoneText()) - end - - local text = string.format("%s killed %s with %s in %s", - tostring(source), - tostring(destination), - tostring(spellName), - tostring(zone)) - - ---@type Message - local msg = { - channel = "CHANNEL", - data = Heimdall_Data.config.deathReporter.notifyChannel, - message = text, - } - table.insert(shared.messenger.queue, msg) - - if Heimdall_Data.config.deathReporter.doWhisper then - for _, name in pairs(Heimdall_Data.config.whisperNotify) do - local msg = { - channel = "WHISPER", - data = name, - message = text, - } - table.insert(shared.messenger.queue, msg) - end - end - end) - end - - local cleuFrame = CreateFrame("Frame") - cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") - cleuFrame:SetScript("OnEvent", function(self, event, ...) - if not Heimdall_Data.config.deathReporter.enabled then return end - local overkill, err = CLEUParser.GetOverkill(...) - if not err and overkill > 0 then - local source, err = CLEUParser.GetSourceName(...) - if err then source = "unknown" end - local destination, err = CLEUParser.GetDestName(...) - if err then destination = "unknown" end - local spellName, err = CLEUParser.GetSpellName(...) - if err then spellName = "unknown" end - local sourceGUID, err = CLEUParser.GetSourceGUID(...) - if err or not string.match(sourceGUID, "Player") then return end - local destinationGUID, err = CLEUParser.GetDestGUID(...) - if err or not string.match(destinationGUID, "Player") then return end - RegisterDeath(source, destination, spellName) - end - end) - - local systemMessageFrame = CreateFrame("Frame") - systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM") - systemMessageFrame:SetScript("OnEvent", function(self, event, msg) - if not Heimdall_Data.config.deathReporter.enabled then return end - local source, destination = string.match(msg, "(.+) has defeated (.+) in a duel") - if source and destination then - print(string.format("Detected duel between %s and %s", source, destination)) - local now = GetTime() - recentDuels[source] = now - recentDuels[destination] = now - end - end) - - print("Heimdall - DeathReporter loaded") -end +local addonname, shared = ... +---@cast shared HeimdallShared +---@cast addonname string + +---@diagnostic disable-next-line: missing-fields +shared.DeathReporter = {} +function shared.DeathReporter.Init() +-- if not Heimdall_Data.config.deathReporter.enabled then +-- print("Heimdall - DeathReporter disabled") +-- return +-- end + + ---@type table + local recentDeaths = {} + ---@type table + local recentDuels = {} + + ---@param source string + ---@param destination string + ---@param spellName string + local function RegisterDeath(source, destination, spellName) + if not Heimdall_Data.config.deathReporter.enabled then return end + if recentDeaths[destination] + and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle then + return + end + + if recentDuels[destination] + and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then + print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) + return + end + if recentDuels[source] + and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then + print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) + return + end + + recentDeaths[destination] = GetTime() + C_Timer.NewTimer(3, function() + if recentDuels[destination] + and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then + print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) + return + end + if recentDuels[source] + and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then + print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination)) + return + end + + local zone = Heimdall_Data.config.deathReporter.zoneOverride + if not zone then + zone = string.format("%s (%s)", GetZoneText(), GetSubZoneText()) + end + + local text = string.format("%s killed %s with %s in %s", + tostring(source), + tostring(destination), + tostring(spellName), + tostring(zone)) + + ---@type Message + local msg = { + channel = "CHANNEL", + data = Heimdall_Data.config.deathReporter.notifyChannel, + message = text, + } + table.insert(shared.messenger.queue, msg) + + if Heimdall_Data.config.deathReporter.doWhisper then + for _, name in pairs(Heimdall_Data.config.whisperNotify) do + local msg = { + channel = "WHISPER", + data = name, + message = text, + } + table.insert(shared.messenger.queue, msg) + end + end + end) + end + + local cleuFrame = CreateFrame("Frame") + cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") + cleuFrame:SetScript("OnEvent", function(self, event, ...) + if not Heimdall_Data.config.deathReporter.enabled then return end + local overkill, err = CLEUParser.GetOverkill(...) + if not err and overkill > 0 then + local source, err = CLEUParser.GetSourceName(...) + if err then source = "unknown" end + local destination, err = CLEUParser.GetDestName(...) + if err then destination = "unknown" end + local spellName, err = CLEUParser.GetSpellName(...) + if err then spellName = "unknown" end + local sourceGUID, err = CLEUParser.GetSourceGUID(...) + if err or not string.match(sourceGUID, "Player") then return end + local destinationGUID, err = CLEUParser.GetDestGUID(...) + if err or not string.match(destinationGUID, "Player") then return end + RegisterDeath(source, destination, spellName) + end + end) + + local systemMessageFrame = CreateFrame("Frame") + systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM") + systemMessageFrame:SetScript("OnEvent", function(self, event, msg) + if not Heimdall_Data.config.deathReporter.enabled then return end + local source, destination = string.match(msg, "(.+) has defeated (.+) in a duel") + if source and destination then + print(string.format("Detected duel between %s and %s", source, destination)) + local now = GetTime() + recentDuels[source] = now + recentDuels[destination] = now + end + end) + + print("Heimdall - DeathReporter loaded") +end diff --git a/Modules/Dueler.lua b/Modules/Dueler.lua new file mode 100644 index 0000000..e69de29 diff --git a/DumpTable.lua b/Modules/DumpTable.lua similarity index 95% rename from DumpTable.lua rename to Modules/DumpTable.lua index dc92647..58781dc 100644 --- a/DumpTable.lua +++ b/Modules/DumpTable.lua @@ -1,29 +1,29 @@ -local addonname, shared = ... ----@cast shared HeimdallShared ----@cast addonname string - -if not shared.dumpTable then - ---@param table table - ---@param depth number? - shared.dumpTable = function(table, depth) - if not table then - print(tostring(table)) - return - end - if depth == nil then - depth = 0 - end - if (depth > 200) then - print("Error: Depth > 200 in dumpTable()") - return - end - for k, v in pairs(table) do - if (type(v) == "table") then - print(string.rep(" ", depth) .. k .. ":") - shared.dumpTable(v, depth + 1) - else - print(string.rep(" ", depth) .. k .. ": ", v) - end - end - end -end +local addonname, shared = ... +---@cast shared HeimdallShared +---@cast addonname string + +if not shared.dumpTable then + ---@param table table + ---@param depth number? + shared.dumpTable = function(table, depth) + if not table then + print(tostring(table)) + return + end + if depth == nil then + depth = 0 + end + if (depth > 200) then + print("Error: Depth > 200 in dumpTable()") + return + end + for k, v in pairs(table) do + if (type(v) == "table") then + print(string.rep(" ", depth) .. k .. ":") + shared.dumpTable(v, depth + 1) + else + print(string.rep(" ", depth) .. k .. ": ", v) + end + end + end +end diff --git a/Inviter.lua b/Modules/Inviter.lua similarity index 100% rename from Inviter.lua rename to Modules/Inviter.lua diff --git a/Messenger.lua b/Modules/Messenger.lua similarity index 96% rename from Messenger.lua rename to Modules/Messenger.lua index ed12622..4aa37ec 100644 --- a/Messenger.lua +++ b/Modules/Messenger.lua @@ -1,107 +1,107 @@ -local addonname, shared = ... ----@cast shared HeimdallShared ----@cast addonname string - ----@diagnostic disable-next-line: missing-fields -shared.Messenger = {} -function shared.Messenger.Init() - -- if not Heimdall_Data.config.messenger.enabled then - -- print("Heimdall - Messenger disabled") - -- return - -- end - - ---@class Message - ---@field message string - ---@field channel string - ---@field data string|number - - ---@type table - local channelIdMap = {} - - local FindOrJoinChannel = function(channelName, password) - local function GetChannelId(channelName) - local channels = { GetChannelList() } - for i = 1, #channels, 2 do - local id = channels[i] - local name = channels[i + 1] - if name == channelName then - return id - end - end - end - - local channelId = GetChannelId(channelName) - if not channelId then - print("Channel", tostring(channelName), "not found, joining") - if password then - JoinPermanentChannel(channelName, password) - else - JoinPermanentChannel(channelName) - end - end - channelId = GetChannelId(channelName) - channelIdMap[channelName] = channelId - return channelId - end - - local ScanChannels = function() - local channels = { GetChannelList() } - for i = 1, #channels, 2 do - local id = channels[i] - local name = channels[i + 1] - channelIdMap[name] = id - end - end - - if not shared.messenger then shared.messenger = {} end - if not shared.messenger.queue then shared.messenger.queue = {} end - if not shared.messenger.ticker then - local function DoMessage() - if not Heimdall_Data.config.messenger.enabled then return end - ---@type Message - local message = shared.messenger.queue[1] - if not message then return end - if not message.message or message.message == "" then return end - if not message.channel or message.channel == "" then return end - - -- Map channel names to ids - if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then - print("Channel presented as string:", message.data) - local channelId = channelIdMap[message.data] - if not channelId then - print("Channel not found, scanning") - ScanChannels() - channelId = channelIdMap[message.data] - end - if not channelId then - print("Channel not joined, joining") - channelId = FindOrJoinChannel(message.data) - end - print("Channel resolved to id", channelId) - message.data = channelId - end - - table.remove(shared.messenger.queue, 1) - if not message.message or message.message == "" then return end - if not message.channel or message.channel == "" then return end - if not message.data or message.data == "" then return end - SendChatMessage(message.message, message.channel, nil, message.data) - end - local function Tick() - DoMessage() - shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1) - end - Tick() - end - - --C_Timer.NewTicker(2, function() - -- print("Q") - -- table.insert(data.messenger.queue, { - -- channel = "CHANNEL", - -- data = "Foobar", - -- message = "TEST" - -- }) - --end) - - print("Heimdall - Messenger loaded") -end +local addonname, shared = ... +---@cast shared HeimdallShared +---@cast addonname string + +---@diagnostic disable-next-line: missing-fields +shared.Messenger = {} +function shared.Messenger.Init() + -- if not Heimdall_Data.config.messenger.enabled then + -- print("Heimdall - Messenger disabled") + -- return + -- end + + ---@class Message + ---@field message string + ---@field channel string + ---@field data string|number + + ---@type table + local channelIdMap = {} + + local FindOrJoinChannel = function(channelName, password) + local function GetChannelId(channelName) + local channels = { GetChannelList() } + for i = 1, #channels, 2 do + local id = channels[i] + local name = channels[i + 1] + if name == channelName then + return id + end + end + end + + local channelId = GetChannelId(channelName) + if not channelId then + print("Channel", tostring(channelName), "not found, joining") + if password then + JoinPermanentChannel(channelName, password) + else + JoinPermanentChannel(channelName) + end + end + channelId = GetChannelId(channelName) + channelIdMap[channelName] = channelId + return channelId + end + + local ScanChannels = function() + local channels = { GetChannelList() } + for i = 1, #channels, 2 do + local id = channels[i] + local name = channels[i + 1] + channelIdMap[name] = id + end + end + + if not shared.messenger then shared.messenger = {} end + if not shared.messenger.queue then shared.messenger.queue = {} end + if not shared.messenger.ticker then + local function DoMessage() + if not Heimdall_Data.config.messenger.enabled then return end + ---@type Message + local message = shared.messenger.queue[1] + if not message then return end + if not message.message or message.message == "" then return end + if not message.channel or message.channel == "" then return end + + -- Map channel names to ids + if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then + print("Channel presented as string:", message.data) + local channelId = channelIdMap[message.data] + if not channelId then + print("Channel not found, scanning") + ScanChannels() + channelId = channelIdMap[message.data] + end + if not channelId then + print("Channel not joined, joining") + channelId = FindOrJoinChannel(message.data) + end + print("Channel resolved to id", channelId) + message.data = channelId + end + + table.remove(shared.messenger.queue, 1) + if not message.message or message.message == "" then return end + if not message.channel or message.channel == "" then return end + if not message.data or message.data == "" then return end + SendChatMessage(message.message, message.channel, nil, message.data) + end + local function Tick() + DoMessage() + shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1) + end + Tick() + end + + --C_Timer.NewTicker(2, function() + -- print("Q") + -- table.insert(data.messenger.queue, { + -- channel = "CHANNEL", + -- data = "Foobar", + -- message = "TEST" + -- }) + --end) + + print("Heimdall - Messenger loaded") +end diff --git a/Spotter.lua b/Modules/Spotter.lua similarity index 96% rename from Spotter.lua rename to Modules/Spotter.lua index 93174fb..0fe88be 100644 --- a/Spotter.lua +++ b/Modules/Spotter.lua @@ -1,119 +1,119 @@ -local addonname, shared = ... ----@cast shared HeimdallShared ----@cast addonname string - ----@diagnostic disable-next-line: missing-fields -shared.Spotter = {} -function shared.Spotter.Init() --- if not Heimdall_Data.config.spotter.enabled then --- print("Heimdall - Spotter disabled") --- return --- end - - local function FormatHP(hp) - if hp > 1e9 then - return string.format("%.1fB", hp / 1e9) - elseif hp > 1e6 then - return string.format("%.1fM", hp / 1e6) - elseif hp > 1e3 then - return string.format("%.1fK", hp / 1e3) - else - return hp - end - end - - ---@type table - local throttleTable = {} - - ---@param unit string - ---@param name string - ---@param faction string - ---@param hostile boolean - ---@return boolean - ---@return string? error - local function ShouldNotify(unit, name, faction, hostile) - if Heimdall_Data.config.spotter.stinky then - if Heimdall_Data.config.stinkies[name] then return true end - end - if Heimdall_Data.config.spotter.alliance then - if faction == "Alliance" then return true end - end - if Heimdall_Data.config.spotter.hostile then - if hostile then return true end - end - return Heimdall_Data.config.spotter.everyone - end - - ---@param unit string - ---@return string? - local function NotifySpotted(unit) - if not unit then return string.format("Could not find unit %s", tostring(unit)) end - if not UnitIsPlayer(unit) then return nil end - - local name = UnitName(unit) - if not name then return string.format("Could not find name for unit %s", tostring(unit)) end - - local time = GetTime() - if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then - return string.format("Throttled %s", tostring(name)) - end - throttleTable[name] = time - - local race = UnitRace(unit) - if not race then return string.format("Could not find race for unit %s", tostring(unit)) end - local faction = shared.raceMap[race] - if not faction then return string.format("Could not find faction for race %s", tostring(race)) end - - local hostile = UnitCanAttack("player", unit) == 1 - local doNotify = ShouldNotify(unit, name, faction, hostile) - if not doNotify then return string.format("Not notifying for %s", tostring(name)) end - - local hp = UnitHealth(unit) - if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end - - local maxHp = UnitHealthMax(unit) - if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end - - local location = Heimdall_Data.config.spotter.zoneOverride - if not location then - local zone = GetZoneText() - if not zone then return string.format("Could not find zone for unit %s", tostring(unit)) end - local subzone = GetSubZoneText() - if not subzone then subzone = "" end - location = string.format("%s (%s)", zone, subzone) - end - - local stinky = Heimdall_Data.config.stinkies[name] or false - local text = string.format("I see (%s) %s %s of race %s (%s) with health %s/%s at %s", - hostile and "Hostile" or "Friendly", - stinky and string.format("(%s)", "!!!!") or "", - name, - race, - faction, - FormatHP(hp), - FormatHP(maxHp), - location) - - ---@type Message - local msg = { - channel = "CHANNEL", - data = Heimdall_Data.config.spotter.notifyChannel, - message = text - } - --shared.dumpTable(msg) - table.insert(shared.messenger.queue, msg) - end - - local frame = CreateFrame("Frame") - frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") - frame:RegisterEvent("TARGET_UNIT_CHANGED") - frame:SetScript("OnEvent", function(self, event, unit) - if not Heimdall_Data.config.spotter.enabled then return end - local err = NotifySpotted(unit) - if err then - print(string.format("Error notifying %s: %s", tostring(unit), tostring(err))) - end - end) - - print("Heimdall - Spotter loaded") -end +local addonname, shared = ... +---@cast shared HeimdallShared +---@cast addonname string + +---@diagnostic disable-next-line: missing-fields +shared.Spotter = {} +function shared.Spotter.Init() +-- if not Heimdall_Data.config.spotter.enabled then +-- print("Heimdall - Spotter disabled") +-- return +-- end + + local function FormatHP(hp) + if hp > 1e9 then + return string.format("%.1fB", hp / 1e9) + elseif hp > 1e6 then + return string.format("%.1fM", hp / 1e6) + elseif hp > 1e3 then + return string.format("%.1fK", hp / 1e3) + else + return hp + end + end + + ---@type table + local throttleTable = {} + + ---@param unit string + ---@param name string + ---@param faction string + ---@param hostile boolean + ---@return boolean + ---@return string? error + local function ShouldNotify(unit, name, faction, hostile) + if Heimdall_Data.config.spotter.stinky then + if Heimdall_Data.config.stinkies[name] then return true end + end + if Heimdall_Data.config.spotter.alliance then + if faction == "Alliance" then return true end + end + if Heimdall_Data.config.spotter.hostile then + if hostile then return true end + end + return Heimdall_Data.config.spotter.everyone + end + + ---@param unit string + ---@return string? + local function NotifySpotted(unit) + if not unit then return string.format("Could not find unit %s", tostring(unit)) end + if not UnitIsPlayer(unit) then return nil end + + local name = UnitName(unit) + if not name then return string.format("Could not find name for unit %s", tostring(unit)) end + + local time = GetTime() + if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then + return string.format("Throttled %s", tostring(name)) + end + throttleTable[name] = time + + local race = UnitRace(unit) + if not race then return string.format("Could not find race for unit %s", tostring(unit)) end + local faction = shared.raceMap[race] + if not faction then return string.format("Could not find faction for race %s", tostring(race)) end + + local hostile = UnitCanAttack("player", unit) == 1 + local doNotify = ShouldNotify(unit, name, faction, hostile) + if not doNotify then return string.format("Not notifying for %s", tostring(name)) end + + local hp = UnitHealth(unit) + if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end + + local maxHp = UnitHealthMax(unit) + if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end + + local location = Heimdall_Data.config.spotter.zoneOverride + if not location then + local zone = GetZoneText() + if not zone then return string.format("Could not find zone for unit %s", tostring(unit)) end + local subzone = GetSubZoneText() + if not subzone then subzone = "" end + location = string.format("%s (%s)", zone, subzone) + end + + local stinky = Heimdall_Data.config.stinkies[name] or false + local text = string.format("I see (%s) %s %s of race %s (%s) with health %s/%s at %s", + hostile and "Hostile" or "Friendly", + stinky and string.format("(%s)", "!!!!") or "", + name, + race, + faction, + FormatHP(hp), + FormatHP(maxHp), + location) + + ---@type Message + local msg = { + channel = "CHANNEL", + data = Heimdall_Data.config.spotter.notifyChannel, + message = text + } + --shared.dumpTable(msg) + table.insert(shared.messenger.queue, msg) + end + + local frame = CreateFrame("Frame") + frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") + frame:RegisterEvent("TARGET_UNIT_CHANGED") + frame:SetScript("OnEvent", function(self, event, unit) + if not Heimdall_Data.config.spotter.enabled then return end + local err = NotifySpotted(unit) + if err then + print(string.format("Error notifying %s: %s", tostring(unit), tostring(err))) + end + end) + + print("Heimdall - Spotter loaded") +end diff --git a/Whoer.lua b/Modules/Whoer.lua similarity index 96% rename from Whoer.lua rename to Modules/Whoer.lua index c79dafa..c222956 100644 --- a/Whoer.lua +++ b/Modules/Whoer.lua @@ -1,428 +1,428 @@ -local addonname, shared = ... ----@cast shared HeimdallShared ----@cast addonname string - ----@diagnostic disable-next-line: missing-fields -shared.Whoer = {} -function shared.Whoer.Init() - -- if not Heimdall_Data.config.who.enabled then - -- print("Heimdall - Whoer disabled") - -- return - -- end - - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - - ---@type table - HeimdallStinkies = {} - - ---@class Player - ---@field name string - ---@field guild string - ---@field race string - ---@field class string - ---@field zone string - ---@field lastSeenInternal number - ---@field lastSeen string - ---@field firstSeen string - ---@field seenCount number - ---@field stinky boolean? - Player = { - ---@param name string - ---@param guild string - ---@param race string - ---@param class string - ---@param zone string - ---@return Player - new = function(name, guild, race, class, zone) - local self = setmetatable({}, { - __index = Player - }) - self.name = name - self.guild = guild - self.race = race - self.class = class - self.zone = zone - self.lastSeenInternal = GetTime() - self.lastSeen = "never" - self.firstSeen = "never" - self.seenCount = 0 - return self - end, - ---@return string - ToString = function(self) - local out = string.format("%s %s %s\nFirst: %s Last: %s Seen: %3d", - shared.padString(self.name, 16, true), - shared.padString(self.guild, 26, false), - shared.padString(self.zone, 26, false), - shared.padString(self.firstSeen, 10, true), - shared.padString(self.lastSeen, 10, true), - self.seenCount) - return string.format("|cFF%s%s|r", shared.classColors[self.class], out) - end, - ---@return string - NotifyMessage = function(self) - local text = string.format( - "%s %s of class %s, race %s (%s) and guild %s in %s, first seen: %s, last seen: %s, times seen: %d", - self.name, - self.stinky and "(!!!!)" or "", - self.class, - self.race, - tostring(shared.raceMap[self.race]), - self.guild, - self.zone, - self.firstSeen, - self.lastSeen, - self.seenCount) - return text - end - } - - ---@class WHOQuery - ---@field query string - ---@field filters WHOFilter[] - WHOQuery = { - ---@param query string - ---@param filters WHOFilter[] - ---@return WHOQuery - new = function(query, filters) - local self = setmetatable({}, { - __index = WHOQuery - }) - self.query = query - self.filters = filters - return self - end - } - - ---@alias WHOFilter fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean - ---@type WHOFilter - local NotSiegeOfOrgrimmarFilter = function(name, guild, level, race, class, zone) - if not zone then - return false - end - return zone ~= "Siege of Orgrimmar" - end - ---@type WHOFilter - local AllianceFilter = function(name, guild, level, race, class, zone) - if not race then return false end - if not shared.raceMap[race] then return false end - return shared.raceMap[race] == "Alliance" - end - - local whoQueryIdx = 1 - ---@type WHOQuery[] - local whoQueries = { - WHOQuery.new("g-\"БеспредеЛ\"", {}), - WHOQuery.new( - "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Human\" r-\"Dwarf\" r-\"Night Elf\"", - { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - WHOQuery.new( - "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Gnome\" r-\"Draenei\" r-\"Worgen\"", - { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - WHOQuery.new( - "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Kul Tiran\" r-\"Dark Iron Dwarf\" r-\"Void Elf\"", - { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - WHOQuery.new( - "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Lightforged Draenei\" r-\"Mechagnome\"", - { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - WHOQuery.new("Kekv Demonboo Dotmada Firobot Verminal Amaterasu Freexe Tomoki", {}) - } - local queryPending = false - local ttl = #whoQueries * 2 - ---@type WHOQuery? - local lastQuery = nil - - ---@param player Player - ---@return string? - local function Notify(player) - if not Heimdall_Data.config.who.enabled then return end - if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end - if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - return string.format("Not notifying for zone %s", - tostring(player.zone)) - end - - local text = player:NotifyMessage() - ---@type Message - local msg = { - channel = "CHANNEL", - data = Heimdall_Data.config.who.notifyChannel, - message = text - } - table.insert(shared.messenger.queue, msg) - - if Heimdall_Data.config.who.doWhisper then - for _, name in pairs(Heimdall_Data.config.whisperNotify) do - ---@type Message - local msg = { - channel = "WHISPER", - data = name, - message = text - } - table.insert(shared.messenger.queue, msg) - end - end - - return nil - end - ---@param player Player - ---@param zone string - ---@return string? - local function NotifyZoneChanged(player, zone) - if not Heimdall_Data.config.who.enabled then return end - if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end - if not Heimdall_Data.config.who.zoneNotifyFor[zone] - and not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - return string.format("Not notifying for zones %s and %s", tostring(zone), tostring(player.zone)) - end - local text = string.format("%s of class %s (%s - %s) and guild %s moved to %s", - player.name, - player.class, - player.race, - shared.raceMap[player.race] or "Unknown", - player.guild, - zone) - - ---@type Message - local msg = { - channel = "CHANNEL", - data = Heimdall_Data.config.who.notifyChannel, - message = text - } - table.insert(shared.messenger.queue, msg) - - if Heimdall_Data.config.who.doWhisper then - for _, name in pairs(Heimdall_Data.config.whisperNotify) do - ---@type Message - local msg = { - channel = "WHISPER", - data = name, - message = text - } - table.insert(shared.messenger.queue, msg) - end - end - - return nil - end - ---@param player Player - ---@return string? - local function NotifyGone(player) - if not Heimdall_Data.config.who.enabled then return end - if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end - if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - return string.format("Not notifying for zone %s", - tostring(player.zone)) - end - - local text = string.format("%s of class %s and guild %s left %s", - player.name, - player.class, - player.guild, - player.zone) - - ---@type Message - local msg = { - channel = "CHANNEL", - data = Heimdall_Data.config.who.notifyChannel, - message = text - } - table.insert(shared.messenger.queue, msg) - - if Heimdall_Data.config.who.doWhisper then - for _, name in pairs(Heimdall_Data.config.whisperNotify) do - ---@type Message - local msg = { - channel = "WHISPER", - data = name, - message = text - } - table.insert(shared.messenger.queue, msg) - end - end - - return nil - end - - local frame = CreateFrame("Frame") - frame:RegisterEvent("WHO_LIST_UPDATE") - frame:SetScript("OnEvent", function(self, event, ...) - if not Heimdall_Data.config.who.enabled then return end - ---@type WHOQuery? - local query = lastQuery - if not query then - print("No query wtf?") - return - end - - for i = 1, GetNumWhoResults() do - local name, guild, level, race, class, zone = GetWhoInfo(i) - if Heimdall_Data.who.ignored[name] then return end - local continue = false - - ---@type WHOFilter[] - local filters = query.filters - for _, filter in pairs(filters) do - if not filter(name, guild, level, race, class, zone) then - -- Mega scuffed, yes... - -- But wow does not have gotos - continue = true - end - end - - if not continue then - local timestamp = date("%Y-%m-%dT%H:%M:%S") - local player = HeimdallStinkies[name] - if not player then - player = Player.new(name, guild, race, class, zone) - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - local existing = Heimdall_Data.who.data[name] - - if existing then - player.lastSeen = existing.lastSeen or "never" - player.firstSeen = existing.firstSeen or "never" - player.seenCount = existing.seenCount or 0 - end - if player.firstSeen == "never" then - player.firstSeen = timestamp - end - - local stinky = Heimdall_Data.config.stinkies[name] - if stinky then - player.stinky = true - PlaySoundFile("Interface\\Sounds\\Domination.ogg", "Master") - else - --PlaySoundFile("Interface\\Sounds\\Cloak.ogg", "Master") - end - - local err = Notify(player) - if err then - print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) - end - - player.lastSeen = timestamp - player.seenCount = player.seenCount + 1 - HeimdallStinkies[name] = player - end - - player.lastSeenInternal = GetTime() - if player.zone ~= zone then - local err = NotifyZoneChanged(player, zone) - if err then - print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) - end - end - player.zone = zone - player.lastSeen = timestamp - HeimdallStinkies[name] = player - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - Heimdall_Data.who.data[name] = player - end - end - -- Turns out WA cannot do this ( - -- aura_env.UpdateMacro() - _G["FriendsFrameCloseButton"]:Click() - queryPending = false - end) - - do - local function UpdateStinkies() - for name, player in pairs(HeimdallStinkies) do - if player.lastSeenInternal + Heimdall_Data.config.who.ttl < GetTime() then - NotifyGone(player) - --PlaySoundFile("Interface\\Sounds\\Uncloak.ogg", "Master") - HeimdallStinkies[name] = nil - end - end - end - local function Tick() - UpdateStinkies() - C_Timer.NewTimer(0.5, Tick, 1) - end - Tick() - end - - do - local function DoQuery() - if not Heimdall_Data.config.who.enabled then return end - if queryPending then - print("Tried running a who query while one is already pending, previous query:") - shared.dumpTable(lastQuery) - return - end - queryPending = true - - local query = whoQueries[whoQueryIdx] - whoQueryIdx = whoQueryIdx + 1 - if whoQueryIdx > #whoQueries then - whoQueryIdx = 1 - end - lastQuery = query - --print(string.format("Running who query: %s", tostring(query.query))) - SetWhoToUI(1) - SendWho(query.query) - end - local function Tick() - DoQuery() - C_Timer.NewTimer(1, Tick, 1) - end - Tick() - end - - local whoQueryWhisperFrame = CreateFrame("Frame") - whoQueryWhisperFrame:RegisterEvent("CHAT_MSG_WHISPER") - whoQueryWhisperFrame:SetScript("OnEvent", function(self, event, msg, sender) - if not Heimdall_Data.config.who.enabled then return end - if msg == "who" then - for _, player in pairs(HeimdallStinkies) do - local text = player:NotifyMessage() - ---@type Message - local msg = { - channel = "WHISPER", - data = sender, - message = text - } - table.insert(shared.messenger.queue, msg) - end - end - end) - - local whoQueryChannelFrame = CreateFrame("Frame") - whoQueryChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") - whoQueryChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) - if not Heimdall_Data.config.who.enabled then return end - local channelId = select(6, ...) - local channelname = "" - ---@type any[] - local channels = { GetChannelList() } - for i = 1, #channels, 2 do - ---@type number - local id = channels[i] - ---@type string - local name = channels[i + 1] - if id == channelId then - channelname = name - end - end - - if channelname ~= Heimdall_Data.config.who.notifyChannel then return end - - if msg == "who" then - for _, player in pairs(HeimdallStinkies) do - local text = player:NotifyMessage() - ---@type Message - local msg = { - channel = "CHANNEL", - data = channelname, - message = text - } - table.insert(shared.messenger.queue, msg) - end - end - end) - - print("Heimdall - Whoer loaded") -end +local addonname, shared = ... +---@cast shared HeimdallShared +---@cast addonname string + +---@diagnostic disable-next-line: missing-fields +shared.Whoer = {} +function shared.Whoer.Init() + -- if not Heimdall_Data.config.who.enabled then + -- print("Heimdall - Whoer disabled") + -- return + -- end + + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end + + ---@type table + HeimdallStinkies = {} + + ---@class Player + ---@field name string + ---@field guild string + ---@field race string + ---@field class string + ---@field zone string + ---@field lastSeenInternal number + ---@field lastSeen string + ---@field firstSeen string + ---@field seenCount number + ---@field stinky boolean? + Player = { + ---@param name string + ---@param guild string + ---@param race string + ---@param class string + ---@param zone string + ---@return Player + new = function(name, guild, race, class, zone) + local self = setmetatable({}, { + __index = Player + }) + self.name = name + self.guild = guild + self.race = race + self.class = class + self.zone = zone + self.lastSeenInternal = GetTime() + self.lastSeen = "never" + self.firstSeen = "never" + self.seenCount = 0 + return self + end, + ---@return string + ToString = function(self) + local out = string.format("%s %s %s\nFirst: %s Last: %s Seen: %3d", + shared.padString(self.name, 16, true), + shared.padString(self.guild, 26, false), + shared.padString(self.zone, 26, false), + shared.padString(self.firstSeen, 10, true), + shared.padString(self.lastSeen, 10, true), + self.seenCount) + return string.format("|cFF%s%s|r", shared.classColors[self.class], out) + end, + ---@return string + NotifyMessage = function(self) + local text = string.format( + "%s %s of class %s, race %s (%s) and guild %s in %s, first seen: %s, last seen: %s, times seen: %d", + self.name, + self.stinky and "(!!!!)" or "", + self.class, + self.race, + tostring(shared.raceMap[self.race]), + self.guild, + self.zone, + self.firstSeen, + self.lastSeen, + self.seenCount) + return text + end + } + + ---@class WHOQuery + ---@field query string + ---@field filters WHOFilter[] + WHOQuery = { + ---@param query string + ---@param filters WHOFilter[] + ---@return WHOQuery + new = function(query, filters) + local self = setmetatable({}, { + __index = WHOQuery + }) + self.query = query + self.filters = filters + return self + end + } + + ---@alias WHOFilter fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean + ---@type WHOFilter + local NotSiegeOfOrgrimmarFilter = function(name, guild, level, race, class, zone) + if not zone then + return false + end + return zone ~= "Siege of Orgrimmar" + end + ---@type WHOFilter + local AllianceFilter = function(name, guild, level, race, class, zone) + if not race then return false end + if not shared.raceMap[race] then return false end + return shared.raceMap[race] == "Alliance" + end + + local whoQueryIdx = 1 + ---@type WHOQuery[] + local whoQueries = { + WHOQuery.new("g-\"БеспредеЛ\"", {}), + WHOQuery.new( + "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Human\" r-\"Dwarf\" r-\"Night Elf\"", + { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + WHOQuery.new( + "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Gnome\" r-\"Draenei\" r-\"Worgen\"", + { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + WHOQuery.new( + "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Kul Tiran\" r-\"Dark Iron Dwarf\" r-\"Void Elf\"", + { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + WHOQuery.new( + "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Lightforged Draenei\" r-\"Mechagnome\"", + { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + WHOQuery.new("Kekv Demonboo Dotmada Firobot Verminal Amaterasu Freexe Tomoki", {}) + } + local queryPending = false + local ttl = #whoQueries * 2 + ---@type WHOQuery? + local lastQuery = nil + + ---@param player Player + ---@return string? + local function Notify(player) + if not Heimdall_Data.config.who.enabled then return end + if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end + if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + return string.format("Not notifying for zone %s", + tostring(player.zone)) + end + + local text = player:NotifyMessage() + ---@type Message + local msg = { + channel = "CHANNEL", + data = Heimdall_Data.config.who.notifyChannel, + message = text + } + table.insert(shared.messenger.queue, msg) + + if Heimdall_Data.config.who.doWhisper then + for _, name in pairs(Heimdall_Data.config.whisperNotify) do + ---@type Message + local msg = { + channel = "WHISPER", + data = name, + message = text + } + table.insert(shared.messenger.queue, msg) + end + end + + return nil + end + ---@param player Player + ---@param zone string + ---@return string? + local function NotifyZoneChanged(player, zone) + if not Heimdall_Data.config.who.enabled then return end + if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end + if not Heimdall_Data.config.who.zoneNotifyFor[zone] + and not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + return string.format("Not notifying for zones %s and %s", tostring(zone), tostring(player.zone)) + end + local text = string.format("%s of class %s (%s - %s) and guild %s moved to %s", + player.name, + player.class, + player.race, + shared.raceMap[player.race] or "Unknown", + player.guild, + zone) + + ---@type Message + local msg = { + channel = "CHANNEL", + data = Heimdall_Data.config.who.notifyChannel, + message = text + } + table.insert(shared.messenger.queue, msg) + + if Heimdall_Data.config.who.doWhisper then + for _, name in pairs(Heimdall_Data.config.whisperNotify) do + ---@type Message + local msg = { + channel = "WHISPER", + data = name, + message = text + } + table.insert(shared.messenger.queue, msg) + end + end + + return nil + end + ---@param player Player + ---@return string? + local function NotifyGone(player) + if not Heimdall_Data.config.who.enabled then return end + if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end + if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + return string.format("Not notifying for zone %s", + tostring(player.zone)) + end + + local text = string.format("%s of class %s and guild %s left %s", + player.name, + player.class, + player.guild, + player.zone) + + ---@type Message + local msg = { + channel = "CHANNEL", + data = Heimdall_Data.config.who.notifyChannel, + message = text + } + table.insert(shared.messenger.queue, msg) + + if Heimdall_Data.config.who.doWhisper then + for _, name in pairs(Heimdall_Data.config.whisperNotify) do + ---@type Message + local msg = { + channel = "WHISPER", + data = name, + message = text + } + table.insert(shared.messenger.queue, msg) + end + end + + return nil + end + + local frame = CreateFrame("Frame") + frame:RegisterEvent("WHO_LIST_UPDATE") + frame:SetScript("OnEvent", function(self, event, ...) + if not Heimdall_Data.config.who.enabled then return end + ---@type WHOQuery? + local query = lastQuery + if not query then + print("No query wtf?") + return + end + + for i = 1, GetNumWhoResults() do + local name, guild, level, race, class, zone = GetWhoInfo(i) + if Heimdall_Data.who.ignored[name] then return end + local continue = false + + ---@type WHOFilter[] + local filters = query.filters + for _, filter in pairs(filters) do + if not filter(name, guild, level, race, class, zone) then + -- Mega scuffed, yes... + -- But wow does not have gotos + continue = true + end + end + + if not continue then + local timestamp = date("%Y-%m-%dT%H:%M:%S") + local player = HeimdallStinkies[name] + if not player then + player = Player.new(name, guild, race, class, zone) + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end + local existing = Heimdall_Data.who.data[name] + + if existing then + player.lastSeen = existing.lastSeen or "never" + player.firstSeen = existing.firstSeen or "never" + player.seenCount = existing.seenCount or 0 + end + if player.firstSeen == "never" then + player.firstSeen = timestamp + end + + local stinky = Heimdall_Data.config.stinkies[name] + if stinky then + player.stinky = true + PlaySoundFile("Interface\\Sounds\\Domination.ogg", "Master") + else + --PlaySoundFile("Interface\\Sounds\\Cloak.ogg", "Master") + end + + local err = Notify(player) + if err then + print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) + end + + player.lastSeen = timestamp + player.seenCount = player.seenCount + 1 + HeimdallStinkies[name] = player + end + + player.lastSeenInternal = GetTime() + if player.zone ~= zone then + local err = NotifyZoneChanged(player, zone) + if err then + print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) + end + end + player.zone = zone + player.lastSeen = timestamp + HeimdallStinkies[name] = player + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end + Heimdall_Data.who.data[name] = player + end + end + -- Turns out WA cannot do this ( + -- aura_env.UpdateMacro() + _G["FriendsFrameCloseButton"]:Click() + queryPending = false + end) + + do + local function UpdateStinkies() + for name, player in pairs(HeimdallStinkies) do + if player.lastSeenInternal + Heimdall_Data.config.who.ttl < GetTime() then + NotifyGone(player) + --PlaySoundFile("Interface\\Sounds\\Uncloak.ogg", "Master") + HeimdallStinkies[name] = nil + end + end + end + local function Tick() + UpdateStinkies() + C_Timer.NewTimer(0.5, Tick, 1) + end + Tick() + end + + do + local function DoQuery() + if not Heimdall_Data.config.who.enabled then return end + if queryPending then + print("Tried running a who query while one is already pending, previous query:") + shared.dumpTable(lastQuery) + return + end + queryPending = true + + local query = whoQueries[whoQueryIdx] + whoQueryIdx = whoQueryIdx + 1 + if whoQueryIdx > #whoQueries then + whoQueryIdx = 1 + end + lastQuery = query + --print(string.format("Running who query: %s", tostring(query.query))) + SetWhoToUI(1) + SendWho(query.query) + end + local function Tick() + DoQuery() + C_Timer.NewTimer(1, Tick, 1) + end + Tick() + end + + local whoQueryWhisperFrame = CreateFrame("Frame") + whoQueryWhisperFrame:RegisterEvent("CHAT_MSG_WHISPER") + whoQueryWhisperFrame:SetScript("OnEvent", function(self, event, msg, sender) + if not Heimdall_Data.config.who.enabled then return end + if msg == "who" then + for _, player in pairs(HeimdallStinkies) do + local text = player:NotifyMessage() + ---@type Message + local msg = { + channel = "WHISPER", + data = sender, + message = text + } + table.insert(shared.messenger.queue, msg) + end + end + end) + + local whoQueryChannelFrame = CreateFrame("Frame") + whoQueryChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") + whoQueryChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) + if not Heimdall_Data.config.who.enabled then return end + local channelId = select(6, ...) + local channelname = "" + ---@type any[] + local channels = { GetChannelList() } + for i = 1, #channels, 2 do + ---@type number + local id = channels[i] + ---@type string + local name = channels[i + 1] + if id == channelId then + channelname = name + end + end + + if channelname ~= Heimdall_Data.config.who.notifyChannel then return end + + if msg == "who" then + for _, player in pairs(HeimdallStinkies) do + local text = player:NotifyMessage() + ---@type Message + local msg = { + channel = "CHANNEL", + data = channelname, + message = text + } + table.insert(shared.messenger.queue, msg) + end + end + end) + + print("Heimdall - Whoer loaded") +end diff --git a/deploy.sh b/deploy.sh index 019d39a..7248f3a 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,5 +1,5 @@ rm Heimdall.zip mkdir Heimdall -cp *.lua *.toc Heimdall +cp *.lua *.toc Modules/*.lua Heimdall 7z a Heimdall.zip Heimdall rm -rf Heimdall \ No newline at end of file