diff --git a/Classes/Commands.lua b/Classes/Commands.lua index 16bfbdb4..05b0dbea 100644 --- a/Classes/Commands.lua +++ b/Classes/Commands.lua @@ -23,6 +23,7 @@ GL.Commands = GL.Commands or { setdisenchanter = "Sets the disenchanter entry directly without having to select one: /gl sd [mydisenchanter]", dft = "Open the DFT importer. Data exported from your DFT sheet can be imported here", cpr = "Open the classicpr importer. Data exported from https://classicpr.io/ can be imported here", + rrobin = "Open the RRobin importer. Data exported from https://rrobin.net/ can be imported here", export = "Export dropped loot to a CSV format which is compatible with TMB for example.", groups = "Open the group window where you can provide a roster from csv/raidhelper/wowhead so that you can: see who's missing and sort groups automatically", import = "Opens the general import window that includes shortcuts to the TMB, SoftRes or loot priority importers", @@ -262,6 +263,9 @@ GL.Commands = GL.Commands or { -- Open the TMB window with CPR skin cpr = function () GL.TMB:draw("cpr"); end, + -- Open the TMB window with RRobin skin + rrobin = function () GL.TMB:draw("rrobin"); end, + -- Open the pack mule window packmule = function () Settings:draw("PackMule"); end, diff --git a/Classes/DroppedLoot.lua b/Classes/DroppedLoot.lua index f312beeb..763d02bd 100644 --- a/Classes/DroppedLoot.lua +++ b/Classes/DroppedLoot.lua @@ -3,24 +3,43 @@ local L = Gargul_L; ---@type GL local _, GL = ...; +---@type Constants +local Constants = GL.Data.Constants; + +---@type Events +local Events = GL.Events; + +---@type SoftRes +local SoftRes = GL.SoftRes; + ---@type TMB local TMB = GL.TMB; +local LCG = LibStub("LibCustomGlowGargul-1.0"); + ---@class DroppedLoot -GL.DroppedLoot = { +local DroppedLoot = { + + ---@type boolean + _initialized = false, + + ---@type boolean + allButtonsHooked = false, + + ---@type boolean + lootWindowIsOpened = false, + + ---@type table Announced = {}, - initialized = false, + + ---@type table ButtonsHooked = {}, - allButtonsHooked = false, + + ---@type table LootButtonItemLinkCache = {}, - lootWindowIsOpened = false, }; -local Constants = GL.Data.Constants; ---@type Data -local DroppedLoot = GL.DroppedLoot; ---@type DroppedLoot -local Events = GL.Events; ---@type Events -local SoftRes = GL.SoftRes; ---@type SoftRes -local LCG = LibStub("LibCustomGlowGargul-1.0"); +GL.DroppedLoot = DroppedLoot; ---@return boolean function DroppedLoot:_init() @@ -48,7 +67,7 @@ function DroppedLoot:_init() -- Make sure to keep track of the loot window status Events:register("DroppedLootLootClosedListener", "LOOT_CLOSED", function () - DroppedLoot.lootWindowIsOpened = false; + self.lootWindowIsOpened = false; GL.Interface.ShortcutKeysLegend:close(); end); @@ -57,7 +76,7 @@ function DroppedLoot:_init() Events:register("DroppedLootLootClosedHighlighterListener", "LOOT_CLOSED", function () self:removeHighlights(); - self.DroppedLoot.LootButtonItemLinkCache = {}; + self.LootButtonItemLinkCache = {}; end ); @@ -69,8 +88,6 @@ function DroppedLoot:_init() end --- Fired when a loot window is opened ---- ----@return void function DroppedLoot:lootReady() self.lootWindowIsOpened = true; @@ -123,7 +140,7 @@ end -- The player navigated to a different page in the loot window function DroppedLoot:lootChanged() local lootChanged = false; - for buttonIndex = 1, _G.LOOTFRAME_NUMBUTTONS do + for buttonIndex = 1, LOOTFRAME_NUMBUTTONS do local buttonName = "LootButton" .. buttonIndex; local Button = getglobal("LootButton" .. buttonIndex); local itemLink = ""; @@ -150,7 +167,6 @@ function DroppedLoot:lootChanged() end --- Remove the highlights on all loot buttons ----@return void function DroppedLoot:removeHighlights() GL:debug("DroppedLoot:removeHighlights"); @@ -158,7 +174,7 @@ function DroppedLoot:removeHighlights() return; end - for buttonIndex = 1, _G.LOOTFRAME_NUMBUTTONS do + for buttonIndex = 1, LOOTFRAME_NUMBUTTONS do local Button = getglobal("LootButton" .. buttonIndex); if (Button) then @@ -173,7 +189,7 @@ function DroppedLoot:highlightItemsOfInterest() -- 4 is the max since buttons seem to be reused -- throughout loot pages... thanks Blizzard - for buttonIndex = 1, _G.LOOTFRAME_NUMBUTTONS do + for buttonIndex = 1, LOOTFRAME_NUMBUTTONS do local Button = getglobal("LootButton" .. buttonIndex); if (Button and Button:IsVisible() and Button.slot) then @@ -215,7 +231,7 @@ function DroppedLoot:hookClickEvents() --- Note: the default UI only supports 4 actionable buttons. --- Even though Add-ons like XLoot show more than 4 at a time, you can only interact with the first 4 - for buttonIndex = 1, _G.LOOTFRAME_NUMBUTTONS do + for buttonIndex = 1, LOOTFRAME_NUMBUTTONS do self.ButtonsHooked[buttonProvider] = self.ButtonsHooked[buttonProvider] or {}; if (not self.ButtonsHooked[buttonProvider][buttonIndex]) then @@ -255,7 +271,8 @@ function DroppedLoot:hookClickEvents() end end --- Announce the loot that dropped in the party or raid chat +--- Announce the loot that dropped in the party or raid chat +---@param Modifiers? table We can use this to override any of the global methods, only for testing purposes function DroppedLoot:announce(Modifiers) Modifiers = Modifiers or {}; local Functions = Modifiers.Functions or { @@ -268,11 +285,9 @@ function DroppedLoot:announce(Modifiers) -- The sourceGUID is something we use to make sure -- we don't announce the same loot multiple times + ---@type string|false local sourceGUID = false; - -- Fetch the name of everyone currently in the raid/party - local PlayersInRaid = GL.User:groupMemberNames(); - -- Get the total number of items that dropped local itemCount = Functions.GetNumLootItems(); @@ -280,14 +295,11 @@ function DroppedLoot:announce(Modifiers) for lootIndex = 1, itemCount do local itemLink = Functions.GetLootSlotLink(lootIndex); - -- This self-executing anonymous function gives us a bit more control (function () - local itemIsHardReserved = false; - -- We need an itemLink to work with. If an item doesn't have it that means: -- It's not a "real" item but gold/silver/copper -- The item no longer exists in the loot window - -- The data is not yet available + -- The data is not (yet) available if (not itemLink) then return; end @@ -295,49 +307,40 @@ function DroppedLoot:announce(Modifiers) -- Make sure we don't override sourceGUID with false/nil if it was already set! sourceGUID = sourceGUID or Functions.GetLootSourceInfo(lootIndex); - -- We're missing crucial data, skip! if (GL:empty(sourceGUID) -- Make sure we have a sourceGUID - or DroppedLoot.Announced[sourceGUID] -- We apparently already announced these items + or self.Announced[sourceGUID] -- We already announced these items ) then return; end - local quality = select(5, Functions.GetLootSlotInfo(lootIndex)) or 0; - local lootType = Functions.GetLootSlotType(lootIndex); - local isReserved = SoftRes:linkIsReserved(itemLink); - local TMBInfo = TMB:byItemLink(itemLink); - - -- Check if we need to announce this item + -- Double check itemID value just in case! local itemID = tonumber(GL:getItemIDFromLink(itemLink)) or 0; - - -- Double checking just in case! if (not GL:higherThanZero(itemID)) then return; end - -- Make sure we're dealing with an actual item here, not currency for example - if (lootType ~= LOOT_SLOT_ITEM) then + -- Make sure we're dealing with an actual item, not currency for example + if (Functions.GetLootSlotType(lootIndex) ~= LOOT_SLOT_ITEM) then return; end - if (( - quality < GL.Settings:get("DroppedLoot.minimumQualityOfAnnouncedLoot", 4) -- Quality is lower than our set minimum - or GL:inTable(Constants.ItemsThatShouldntBeAnnounced, itemID) -- We don't want to announce this item - ) - and not isReserved -- No one (hard)reserved it - and GL:empty(TMBInfo) -- No one has it on his wishlist and it's not a prio item + local TMBInfo; + local IDIsReserved = SoftRes:idIsReserved(itemID); + local quality = select(5, Functions.GetLootSlotInfo(lootIndex)) or 0; + if (quality < GL.Settings:get("DroppedLoot.minimumQualityOfAnnouncedLoot", 4) -- Quality is lower than our set minimum + or GL:inTable(Constants.ItemsThatShouldntBeAnnounced, itemID) -- We don't want to announce this item ) then - return; - end + -- See if there's any TMB data worth sharing in chat + if (GL.Settings:get("TMB.includePrioListInfoInLootAnnouncement") + or GL.Settings:get("TMB.includeWishListInfoInLootAnnouncement") + ) then + TMBInfo = TMB:byItemID(itemID); + end - -- Fetch the applicable SoftRes data (if any) - local ActiveSoftResDetails = {}; - itemIsHardReserved = SoftRes:IDIsHardReserved(itemID); - if (not itemIsHardReserved -- Only fetch the SoftRes data if the item isn't hard-reserved - and isReserved -- This item wasn't reserved - and GL.Settings:get("SoftRes.announceInfoInChat") -- The player isn't interested in SoftRes data - ) then - ActiveSoftResDetails = SoftRes:playerReserveAmountsByItemID(itemID); + -- There's no softres or TMB data to share, return + if (not IDIsReserved and GL:empty(TMBInfo)) then + return; + end end -- Determine the correct channel for announcing the loot @@ -347,7 +350,11 @@ function DroppedLoot:announce(Modifiers) channel = "RAID_WARNING"; end - -- Either announce the item by itself or state that it's hard-reserved! + --[[ + Announce the dropped item in chat + * Either by itself or state that it's hard-reserved! + ]] + local itemIsHardReserved = SoftRes:IDIsHardReserved(itemID); if (itemIsHardReserved and GL.Settings:get("SoftRes.announceInfoInChat") ) then @@ -367,158 +374,34 @@ function DroppedLoot:announce(Modifiers) --[[ SHOW WHO RESERVED THIS ITEM (SOFTRES) ]] - -- * This data is only available if the user has the announce SoftRes setting enabled - if (not GL:empty(ActiveSoftResDetails)) then - GL:sendChatMessage( - (L.CHAT.SOFTRES_DETAILS):format(table.concat(ActiveSoftResDetails, ", ")), - "GROUP" - ); - end - - -- Fetch the applicable TMB data (if any) - local ActiveWishListDetails = {}; - local ActivePrioListDetails = {}; - local maximumNumberOfAnnouncementEntries = GL.Settings:get("TMB.maximumNumberOfAnnouncementEntries", 5); - if (TMBInfo and ( - GL.Settings:get("TMB.includePrioListInfoInLootAnnouncement") - or GL.Settings:get("TMB.includeWishListInfoInLootAnnouncement") - )) then - ActivePrioListDetails, ActiveWishListDetails = self:getTMBDetails(TMBInfo, PlayersInRaid); - end - local itemIsOnSomeonesPriolist = not GL:empty(ActivePrioListDetails); - local itemIsOnSomeonesWishlist = not GL:empty(ActiveWishListDetails); - - --[[ - SHOW WHO HAS PRIORITY ON THIS ITEM (TMB) - ]] - if (itemIsOnSomeonesPriolist - and GL.Settings:get("TMB.includePrioListInfoInLootAnnouncement") + if (not itemIsHardReserved -- Only fetch the SoftRes data if the item isn't hard-reserved + and IDIsReserved -- And there are any reserves + and GL.Settings:get("SoftRes.announceInfoInChat") -- And the player wants to announce softres data in chat ) then - ActivePrioListDetails = TMB:sortEntries(ActivePrioListDetails); - - local entries = 0; - local entryString = ""; - for _, Entry in pairs(ActivePrioListDetails) do - entries = entries + 1; - - -- Add the player to the list (first entry should not start with a comma) - if (GL:empty(entryString)) then - entryString = GL:capitalize(Entry.player); - else - entryString = entryString .. ", " .. GL:capitalize(Entry.player); - end - - -- The user only wants to see a limited number of entries, break! - if (entries >= maximumNumberOfAnnouncementEntries) then - break; - end + local ActiveSoftResDetails = SoftRes:playerReserveAmountsByItemID(itemID); + + -- * This data is only available if the user has the announce SoftRes setting enabled + if (not GL:empty(ActiveSoftResDetails)) then + GL:sendChatMessage( + (L.CHAT.SOFTRES_DETAILS):format(table.concat(ActiveSoftResDetails, ", ")), + "GROUP" + ); end - - GL:sendChatMessage( - (L.CHAT.TMB_PRIORITY_DETAILS):format(TMB:source(), entryString), - "GROUP" - ); end - --[[ - SHOW WHO WISHLISTED THIS ITEM (TMB) - ]] - if (itemIsOnSomeonesWishlist - and GL.Settings:get("TMB.includeWishListInfoInLootAnnouncement") - and (not itemIsOnSomeonesPriolist - or not GL.Settings:get("TMB.hideWishListInfoIfPriorityIsPresent") - ) - ) then - ActiveWishListDetails = TMB:sortEntries(ActiveWishListDetails); - - local entries = 0; - local entryString = ""; - for _, Entry in pairs(ActiveWishListDetails) do - entries = entries + 1; - - -- Add the player to the list (first entry should not start with a comma) - if (GL:empty(entryString)) then - entryString = GL:capitalize(Entry.player); - else - entryString = entryString .. ", " .. GL:capitalize(Entry.player); - end - - -- The user only wants to see a limited number of entries, break! - if (entries >= maximumNumberOfAnnouncementEntries) then - break; - end - end - - GL:sendChatMessage( - (L.CHAT.TMB_WISHLIST_DETAILS):format(entryString), - "GROUP" - ); - end + TMB:announceDetailsOfItemInChat(itemID, TMBInfo); end)(); end -- This ensures that we don't announce the same loot multiple times! -- Keep in mind that sourceGUID can be empty! if (sourceGUID) then - DroppedLoot.Announced[sourceGUID] = true; + self.Announced[sourceGUID] = true; end end ---- Get all TMB details ---- ----@return table, table -function DroppedLoot:getTMBDetails(TMBInfo, PlayersInRaid) - local ActivePrioListDetails = {}; - local ActiveWishListDetails = {}; - - -- Make sure we only show wishlist details of people - -- Who are actually in the raid - for _, Entry in pairs(TMBInfo) do - local playerName = string.lower(Entry.character); - - --- NOTE TO SELF: it's (os) because of the string.lower, if you remove the lower then change below accordingly! - if (not GL.User.isInGroup or GL:inTable(PlayersInRaid, string.gsub(playerName, "%(os%)", ""))) then - local prio = Entry.prio; - local entryType = Entry.type or Constants.tmbTypeWish; - local isOffSpec = GL:strContains(playerName, "%(os%)"); - local prioOffset = 0; - local sortingOrder = tonumber(prio); - - -- We add 100 to the prio (first key) of the object - -- This object is used for sorting later and is not visible to the player - if (isOffSpec) then - prioOffset = 100; - end - - if (not GL:empty(sortingOrder)) then - sortingOrder = prio + prioOffset; - else - -- If for whatever reason we can't determine the - -- item prio then we add it to the end of the list by default - sortingOrder = 1000; - end - - if (entryType == Constants.tmbTypePrio) then - tinsert(ActivePrioListDetails, { - prio = sortingOrder, - player = string.format("%s[%s]", playerName, prio), - }); - else - tinsert(ActiveWishListDetails, { - prio = sortingOrder, - player = string.format("%s[%s]", playerName, prio), - }); - end - end - end - - return ActivePrioListDetails, ActiveWishListDetails; -end - --- This function allows me to test the loot announcement without actually having to kill bosses in raids --- It also enables me to debug issues other players may have using their actual TMB/SoftRes data ---- ----@return void function DroppedLoot:announceTest(...) local itemIDs = ...; diff --git a/Classes/Events.lua b/Classes/Events.lua index be4c5497..e0b6ac14 100644 --- a/Classes/Events.lua +++ b/Classes/Events.lua @@ -2,35 +2,38 @@ local _, GL = ...; ---@class Events -GL.Events = { +local Events = { + ---@type boolean _initialized = false, + + ---@type Frame Frame = nil, + + ---@type table Registry = { + ---@type table EventListeners = {}, + + ---@type table EventByIdentifier = {}, }, }; - -local Events = GL.Events; ---@type Events +GL.Events = Events; --- Prepare the event frame for future use ---- ----@param EventFrame table ----@return void +---@param EventFrame Frame function Events:_init(EventFrame) -- No need to initialize this class twice if (self._initialized) then return; end + self._initialized = true; self.Frame = EventFrame; self.Frame:SetScript("OnEvent", self.listen); - - self._initialized = true; end --- Helper function that turns the mouse button pressed and modifier keys held into a identifier string ---- ---@param mouseButtonPressed string ---@return string function Events:getClickCombination(mouseButtonPressed) @@ -62,7 +65,6 @@ function Events:getClickCombination(mouseButtonPressed) end --- Register an event listener ---- ---@param identifier string|nil ---@param event string ---@param callback function @@ -109,9 +111,7 @@ function Events:register(identifier, event, callback) end --- Unregister a listener based on its identifier ---- ---@param identifier string ----@return void function Events:unregister(identifier) if (type(identifier) == "table") then for _, event in pairs(identifier) do @@ -154,10 +154,8 @@ function Events:unregister(identifier) end --- Fire the event listeners whenever a registered event comes in ---- ---@param event string ----@param . any ----@return void +---@param ... any function Events:listen(event, ...) local args = {...}; @@ -167,7 +165,6 @@ function Events:listen(event, ...) end --- Register multiple event listeners ---- ---@param EventDetails table ---@param callback function ---@return boolean @@ -189,10 +186,8 @@ function Events:massRegister(EventDetails, callback) end --- Fire an event manually (assuming there's a listener for it) ---- ---@param event string ----@param . any ----@return void +---@param ... any function Events:fire(event, ...) self:listen(event, ...); end diff --git a/Classes/RollOff.lua b/Classes/RollOff.lua index d8b54fbb..a56a70ab 100644 --- a/Classes/RollOff.lua +++ b/Classes/RollOff.lua @@ -181,18 +181,25 @@ function RollOff:postStartMessage(itemLink, time, note) end local EligiblePlayers = {}; + local wasImportedFromRRobin = TMB:wasImportedFromRRobin(); if (not GL:empty(PrioListEntries) and GL.Settings:get("TMB.announcePriolistInfoWhenRolling") ) then PrioListEntries = TMB:sortEntries(PrioListEntries); + local topPrio = PrioListEntries[1].prio; for _, Entry in pairs(PrioListEntries) do -- This is the first player in the list, add him if (not EligiblePlayers[1]) then tinsert(EligiblePlayers, Entry); else - -- This players prio is worse than the number one, break! - if (Entry.prio ~= EligiblePlayers[1].prio) then + -- RRobin works slightly differently from normal TMB-compatible loot systems + if (wasImportedFromRRobin and topPrio - Entry.prio > 1) then + break; + end + + -- This player's prio is worse than the number one, break! + if (not wasImportedFromRRobin and Entry.prio ~= topPrio) then break; end @@ -204,6 +211,7 @@ function RollOff:postStartMessage(itemLink, time, note) and GL.Settings:get("TMB.announceWishlistInfoWhenRolling") ) then WishListEntries = TMB:sortEntries(WishListEntries); + local topPrio = WishListEntries[1].prio; for _, Entry in pairs(WishListEntries) do -- This is the first player in the list, add him @@ -211,7 +219,7 @@ function RollOff:postStartMessage(itemLink, time, note) tinsert(EligiblePlayers, Entry); else -- This players position is worse than the number one, break! - if (Entry.prio ~= EligiblePlayers[1].prio) then + if (Entry.prio ~= topPrio) then break; end diff --git a/Classes/Settings.lua b/Classes/Settings.lua index 86bf66da..3bdfe9de 100644 --- a/Classes/Settings.lua +++ b/Classes/Settings.lua @@ -13,16 +13,19 @@ local DB = GL.DB; local Events = GL.Events; ---@class Settings -GL.Settings = { +local Settings = { + + ---@type boolean _initialized = false, - Defaults = GL.Data.DefaultSettings, + + ---@type table Active = {}, -- This object holds the actual setting values applicable to this runtime -}; ----@type Settings -local Settings = GL.Settings; + ---@type table + Defaults = GL.Data.DefaultSettings, +}; +GL.Settings = Settings; ----@return void function Settings:_init() -- No need to initialize this class twice if (self._initialized) then @@ -49,8 +52,6 @@ function Settings:_init() end --- Make sure the settings adhere to our rules ---- ----@return void function Settings:sanitizeSettings() self:enforceTemporarySettings(); @@ -94,8 +95,6 @@ end --- These settings are version-specific and will be removed over time! --- IMPORTANT: don't use self:get/set/has since defaults have not be overwritten yet and .Active is not available ---- ----@return void function Settings:enforceTemporarySettings() --- This is reserved for version-based logic (e.g. cleaning up variables, settings etc.) @@ -306,22 +305,17 @@ function Settings:enforceTemporarySettings() end --- Draw a setting section ---- ---@param section string|nil ---@param onCloseCallback function|nil What to do after closing the settings again ----@return void function Settings:draw(section, onCloseCallback) GL.Interface.Settings.Overview:draw(section, onCloseCallback); end ----@return void function Settings:close() GL.Interface.Settings.Overview:close(); end --- Reset the addon to its default settings ---- ----@return void function Settings:resetToDefault() self.Active = {}; DB:set("Settings", {}); @@ -331,8 +325,6 @@ function Settings:resetToDefault() end --- Override the addon's default settings with the user's custom settings ---- ----@return void function Settings:overrideDefaultsWithUserSettings() -- Reset the currently active settings table self.Active = {}; @@ -350,8 +342,6 @@ end --- We use this method to make sure that the interface is only built --- when the user has actually accessed the settings menu, which doesn't happen every session ---- ----@return void function Settings:showSettingsMenu(Frame) -- Add the addon title to the top of the settings frame local Title = Frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge"); @@ -395,7 +385,6 @@ end --- Get a setting by a given key. Use dot notation to traverse multiple levels e.g: --- Settings.UI.Auctioneer.offsetX can be fetched using Settings:get("Settings.UI.Auctioneer.offsetX") --- without having to worry about tables or keys existing yes or no. ---- ---@param keyString string ---@param default any ---@return any @@ -411,7 +400,6 @@ end --- Set a setting by a given key and value. Use dot notation to traverse multiple levels e.g: --- Settings.UI.Auctioneer.offsetX can be set using Settings:set("Settings.UI.Auctioneer.offsetX", myValue) --- without having to worry about tables or keys existing yes or no. ---- ---@param keyString string ---@param value any ---@param quiet boolean Should trigger event? @@ -429,7 +417,6 @@ end ---@param setting string ---@param func function ----@return void function Settings:onChange(setting, func) Events:register(nil, "GL.SETTING_CHANGED." .. setting, func); end diff --git a/Classes/SoftRes.lua b/Classes/SoftRes.lua index 79f4a0e9..4444f92d 100644 --- a/Classes/SoftRes.lua +++ b/Classes/SoftRes.lua @@ -3,8 +3,20 @@ local L = Gargul_L; ---@type GL local _, GL = ...; +---@type DB +local DB = GL.DB; + +---@type Constants +local Constants = GL.Data.Constants; + +---@type CommActions +local CommActions = Constants.Comm.Actions; + +---@type Settings +local Settings = GL.Settings; ---@type Settings + ---@class SoftRes -GL.SoftRes = { +local SoftRes = { _initialized = false, announcedImportSoftResAt = false, broadcastInProgress = false, @@ -23,12 +35,7 @@ GL.SoftRes = { SoftReservedItemIDs = {}, }, }; - -local DB = GL.DB; ---@type DB -local Constants = GL.Data.Constants; ---@type Data -local CommActions = Constants.Comm.Actions; -local Settings = GL.Settings; ---@type Settings -local SoftRes = GL.SoftRes; ---@type SoftRes +GL.SoftRes = SoftRes; --- @return boolean function SoftRes:_init() @@ -471,7 +478,7 @@ end --- Check whether a given item id is reserved (either soft or hard) --- ---@param itemID number|string ----@param inRaidOnly boolean +---@param inRaidOnly? boolean ---@return boolean function SoftRes:idIsReserved(itemID, inRaidOnly) if (type(inRaidOnly) ~= "boolean") then @@ -585,7 +592,7 @@ end --- Fetch an item's reservations based on its ID --- ---@param itemID number|string ----@param inRaidOnly boolean +---@param inRaidOnly? boolean --- ---@return table --- @@ -639,7 +646,7 @@ end --- Fetch an item's soft reserve amounts in a "Playername (x3)" format --- ---@param itemID number ----@param inRaidOnly boolean +---@param inRaidOnly? boolean ---@return table function SoftRes:playerReserveAmountsByItemID(itemID, inRaidOnly) local SoftReserves = self:byItemID(itemID, inRaidOnly); diff --git a/Classes/TMB.lua b/Classes/TMB.lua index a58246e3..a5c0263b 100644 --- a/Classes/TMB.lua +++ b/Classes/TMB.lua @@ -3,10 +3,10 @@ local L = Gargul_L; ---@type GL local _, GL = ...; -GL.AceGUI = GL.AceGUI or LibStub("AceGUI-3.0"); - ----@type Data +---@type Constants local Constants = GL.Data.Constants; + +---@type CommActions local CommActions = Constants.Comm.Actions; ---@type Settings @@ -16,7 +16,7 @@ local Settings = GL.Settings; local Events = GL.Events; ---@class TMB -GL.TMB = { +local TMB = { _initialized = false, broadcastInProgress = false, lastTooltipData = nil, @@ -24,7 +24,9 @@ GL.TMB = { lastTooltipTime = nil, requestingData = false, }; -local TMB = GL.TMB; ---@type TMB +GL.TMB = TMB; + +local OFFSPEC_IDENTIFIER = "%(os%)"; ---@return boolean function TMB:_init() @@ -36,153 +38,146 @@ function TMB:_init() Events:register("TMBUserJoinedGroupListener", "GL.USER_JOINED_NEW_GROUP", function () self:requestData(); end); end ---- Check whether there is TMB data available ---- +--- Check whether TMB data is available ---@return boolean function TMB:available() return GL:higherThanZero(GL.DB:get("TMB.MetaData.importedAt", 0)); end --- Fetch an item's TMB info based on its ID ---- ---@param itemID number ----@param inRaidOnly boolean|nil +---@param raidersOnly? boolean ---@return table -function TMB:byItemID(itemID, inRaidOnly) +function TMB:byItemID(itemID, raidersOnly) -- An invalid item id was provided itemID = tonumber(itemID); if (not GL:higherThanZero(itemID)) then return {}; end - if (type(inRaidOnly) ~= "boolean") then - inRaidOnly = Settings:get("TMB.hideInfoOfPeopleNotInGroup"); - - if (inRaidOnly - and Settings:get("TMB.showEntriesWhenUsingPrio3") - and self:wasImportedFromCPR() - ) then - inRaidOnly = false; - end - - -- User is not in group and showEntriesWhenSolo is true - if (inRaidOnly - and not GL.User.isInGroup - and GL.Settings:get("TMB.showEntriesWhenSolo") - ) then - inRaidOnly = false; - end - end - -- The item linked to this id can have multiple IDs (head of Onyxia for example) local AllLinkedItemIDs = GL:getLinkedItemsForID(itemID); - local Wishes = {}; + -- Fetch all entries related to this item ID + local Entries = {}; + local Checksums = {}; for _, id in pairs(AllLinkedItemIDs) do id = tostring(id); for _, Entry in pairs(GL.DB:get("TMB.Items." .. tostring(id), {})) do - local playerName = string.lower(GL:nameFormat(Entry.character)); + local checksum = ("%s|%s|%s|%s"):format(id, Entry.character, Entry.prio, Entry.type or Constants.tmbTypeWish); - -- If inRaidOnly is true we need to make sure we only return details of people who are actually in the raid - --- NOTE TO SELF: it's (os) because of the string.lower, if you remove the lower then change below accordingly! - if (not inRaidOnly or GL.User:unitInGroup(string.gsub(playerName, "%(os%)", ""))) then - tinsert(Wishes, Entry); + if (not Checksums[checksum]) then + tinsert(Entries, Entry); + Checksums[checksum] = true; end end end - return Wishes; + -- Filter players who are not in the raid + if (self:shouldOnlyIncludeRaiders(raidersOnly)) then + Entries = self:filterNonRaiders(Entries); + end + + return Entries; end ---- Fetch TMB info for a specific item ID and player ---- ----@param itemID number ----@param player string +--- Fetch an item's TMB info based on its item link +---@param itemLink string +---@param inRaidOnly? boolean|nil ---@return table -function TMB:byItemIDAndPlayer(itemID, player) - -- An invalid item id or name was provided - itemID = tonumber(itemID); - player = strlower(strtrim(player)); - if (not GL:higherThanZero(itemID) - or GL:empty(player) - ) then +function TMB:byItemLink(itemLink, inRaidOnly) + if (GL:empty(itemLink)) then return {}; end - local playerWithoutRealm = GL:nameFormat{ - name = player, - stripRealm = true, - func = strlower - }; + return self:byItemID(GL:getItemIDFromLink(itemLink), inRaidOnly); +end - -- The item linked to this id can have multiple IDs (head of Onyxia for example) - local AllLinkedItemIDs = GL:getLinkedItemsForID(itemID); +--- Determine whether we should only show details for players who are currently in our raid +---@param raidersOnly? boolean +---@return boolean +function TMB:shouldOnlyIncludeRaiders(raidersOnly) + if (type(raidersOnly) == "boolean") then + return raidersOnly; + end - local Processed = {}; - local Entries = {}; - for _, id in pairs(AllLinkedItemIDs) do - id = tostring(id); - for _, Entry in pairs(GL.DB:get("TMB.Items." .. tostring(id), {})) do - --- NOTE TO SELF: it's (os) because of the string.lower, if you remove the lower then change below accordingly! - local playerName = string.gsub(strlower(GL:nameFormat(Entry.character)), "%(os%)", ""); - - if (player == playerName - or playerWithoutRealm == playerName - ) then - local checkSum = string.format('%s||%s||%s', player, tostring(Entry.prio), tostring(Entry.type)); - - -- Make sure we don't add the same player/prio combo more than once - if (not Processed[checkSum]) then - Processed[checkSum] = true; - tinsert(Entries, Entry); - end - end - end + -- Show everything when using prio3/classicpr.io is enabled + if (Settings:get("TMB.showEntriesWhenUsingPrio3") + and self:wasImportedFromCPR() + ) then + return false; end - return Entries; + -- User is not in group and showEntriesWhenSolo is true + if (not GL.User.isInGroup + and GL.Settings:get("TMB.showEntriesWhenSolo") + ) then + return false; + end + + return Settings:get("TMB.hideInfoOfPeopleNotInGroup") ~= false; end ---- Fetch an item's TMB info based on its item link ---- ----@param itemLink string ----@param inRaidOnly boolean|nil +--- Filter entries of players who are not in the raid +---@param Entries table ---@return table -function TMB:byItemLinkAndPlayer(itemLink, player) - if (GL:empty(itemLink)) then - return {}; +function TMB:filterNonRaiders(Entries) + for key, Entry in pairs(Entries or {}) do + local playerName = GL:nameFormat(Entry.character):lower():gsub(OFFSPEC_IDENTIFIER, ""); + + if (not GL.User:unitInGroup(playerName)) then + Entry = nil; + Entries[key] = nil; + end end - return self:byItemIDAndPlayer(GL:getItemIDFromLink(itemLink), player); + return GL:tableValues(Entries); end ---- Fetch an item's TMB info based on its item link ---- ----@param itemLink string ----@param inRaidOnly boolean|nil +--- Fetch TMB info for a specific item ID and player +---@param itemID number +---@param player string ---@return table -function TMB:byItemLink(itemLink, inRaidOnly) - if (GL:empty(itemLink)) then +function TMB:byItemIDAndPlayer(itemID, player) + if (type(player) ~= "string" + or GL:empty(player) + ) then return {}; end - return self:byItemID(GL:getItemIDFromLink(itemLink), inRaidOnly); + local Entries = self:byItemID(itemID, false); + if (GL:empty(Entries)) then + return {}; + end + + local playerWithoutRealm = GL:stripRealm(player):lower(); + for key, Entry in pairs(Entries) do + local playerName = GL:nameFormat(Entry.character):lower():gsub(OFFSPEC_IDENTIFIER, ""); + + if (playerName ~= player + and playerName ~= playerWithoutRealm + ) then + Entry = nil; + Entries[key] = nil; + end + end + + return GL:tableValues(Entries); end ---- Fetch an item's TMB tier based on its item link ---- +--- Fetch TMB info for a specific item link and player ---@param itemLink string ----@return string -function TMB:tierByItemLink(itemLink) +---@param player string +---@return table +function TMB:byItemLinkAndPlayer(itemLink, player) if (GL:empty(itemLink)) then - return ""; + return {}; end - return self:tierByItemID(GL:getItemIDFromLink(itemLink)); + return self:byItemIDAndPlayer(GL:getItemIDFromLink(itemLink), player); end --- Fetch an item's TMB tier based on its item ID ---- ---@param itemID string ---@return string function TMB:tierByItemID(itemID) @@ -193,20 +188,18 @@ function TMB:tierByItemID(itemID) return GL.DB:get("TMB.Tiers." .. itemID, ""); end ---- Fetch an item's TMB note based on its item link ---- +--- Fetch an item's TMB tier based on its item link ---@param itemLink string ---@return string -function TMB:noteByItemLink(itemLink) +function TMB:tierByItemLink(itemLink) if (GL:empty(itemLink)) then return ""; end - return self:noteByItemID(GL:getItemIDFromLink(itemLink)); + return self:tierByItemID(GL:getItemIDFromLink(itemLink)); end --- Fetch an item's TMB note based on its item ID ---- ---@param itemID string ---@return string function TMB:noteByItemID(itemID) @@ -239,8 +232,18 @@ function TMB:noteByItemID(itemID) return GL:implode(Notes, "\n"); end +--- Fetch an item's TMB note based on its item link +---@param itemLink string +---@return string +function TMB:noteByItemLink(itemLink) + if (GL:empty(itemLink)) then + return ""; + end + + return self:noteByItemID(GL:getItemIDFromLink(itemLink)); +end + --- Fetch a player's group id and name ---- ---@param player string ---@return number, string|boolean | boolean, boolean function TMB:groupByPlayerName(player) @@ -248,7 +251,7 @@ function TMB:groupByPlayerName(player) return false, false; end - player = string.lower(player); + player = player:lower(); local groupID = GL.DB:get("TMB.PlayerGroup." .. player); if (groupID) then @@ -258,11 +261,100 @@ function TMB:groupByPlayerName(player) return false, false; end +--- Add tooltip lines for RRobin entries +---@param Lines table +---@param Entries table +---@return table +function TMB:RRobinTooltipLines(Lines, Entries) + if (GL:empty(Entries)) then + return {}; + end + + -- Add the header + tinsert(Lines, ("\n|c00FF7A0A%s|r"):format((L.TMB_TOOLTIP_PRIO_HEADER):format(self:source()))); + + local topPrio = Entries[1].prio; + entriesAdded = 0; + for _, Entry in pairs(Entries) do + local priorityColor; + if (entriesAdded == 0) then + -- If there's only one contender, color their priority blue (RARE). Green (UNCOMMON) otherwise + priorityColor = GL.Interface.Colors.RARE; + if (Entries[2] and topPrio - Entries[2].prio <= 1) then + priorityColor = GL.Interface.Colors.UNCOMMON; + end + elseif (topPrio - Entry.prio <= 1) then + priorityColor = GL.Interface.Colors.UNCOMMON; + else + priorityColor = GL.Interface.Colors.POOR; + end + + tinsert(Lines, (" |c00%s%s|r: %s"):format( + priorityColor, + Entry.prio, + GL:nameFormat{ name = Entry.character, colorize = true, defaultColor = Constants.disabledTextColor, } + )); + + entriesAdded = entriesAdded + 1; + if (entriesAdded >= GL.Settings:get("TMB.maximumNumberOfTooltipEntries")) then + break; + end + end + + return Lines; +end + +--- Add tooltip lines for DFT entries +---@param Lines any +---@param Entries any +---@return table +function TMB:DFTTooltipLines(Lines, Entries) + if (GL:empty(Entries)) then + return {}; + end + + -- Add the header + tinsert(Lines, ("\n|c00FF7A0A%s|r"):format((L.TMB_TOOLTIP_PRIO_HEADER):format(self:source()))); + + Entries = self:sortEntries(Entries, 1); + + -- Add the entries to the tooltip + entriesAdded = 0; + for _, Entry in pairs(Entries) do + entriesAdded = entriesAdded + 1; + + local color = GL:classHexColor(GL.Player:classByName(playerName:gsub(OFFSPEC_IDENTIFIER, ""), 0), Constants.disabledTextColor); + tinsert(Lines, ("|c00%s%s[%s]|r"):format( + color, + GL:nameFormat(playerName), + Entry.prio + )); + + -- Make sure we don't add more names to the tooltip than the user allowed + if (entriesAdded >= GL.Settings:get("TMB.maximumNumberOfTooltipEntries")) then + break; + end + end + + return Lines; +end + +--- Add tooltip lines for CPR entries +---@param Lines any +---@param Entries any +---@return table +function TMB:CPRTooltipLines(Lines, Entries) + return self:DFTTooltipLines(Lines, Entries); +end + --- Append the TMB info as defined in GL.DB.TMB.Items to an item's tooltip ---- ---@param itemLink string ---@return table function TMB:tooltipLines(itemLink) + if (not self:available()) then + return {}; + end + -- If we're not in a group there's no point in showing anything! (unless the non-raider setting is active) if ((not GL.User.isInGroup and GL.Settings:get("TMB.hideInfoOfPeopleNotInGroup")) and (not GL.User.isInGroup and not GL.Settings:get("TMB.showEntriesWhenSolo")) @@ -270,66 +362,95 @@ function TMB:tooltipLines(itemLink) return {}; end + local itemID = GL:getItemIDFromLink(itemLink); + if (not itemID) then + return {}; + end + local Lines = {}; - local TMBTier = self:tierByItemLink(itemLink); - local TMBNote = self:noteByItemLink(itemLink); - local TMBInfo = self:byItemLink(itemLink); + local Entries = self:byItemID(itemID); - local TMBHeaderAdded = false; + if (self:shouldOnlyIncludeRaiders()) then + Entries = self:filterNonRaiders(Entries); + end + -- Show item note and tier if (GL.Settings:get("TMB.showItemInfoOnTooltips")) then + local tier = self:tierByItemID(itemID); + local note = self:noteByItemID(itemID); + + local sourceHeaderAdded = false; + local addSourceHeader = function () + if (sourceHeaderAdded) then + return; + end + + tinsert(Lines, ("\n|c00%s%s|r"):format( + Constants.addonHexColor, + self:source() + )); + + sourceHeaderAdded = true; + end; + -- This item has a tier, show it! - if (not GL:empty(TMBTier) - and GL.Data.Constants.TMBTierHexColors[TMBTier] + if (not GL:empty(tier) + and Constants.TMBTierHexColors[tier] ) then - local tierString = string.format("|cFF%s%s|r", - GL.Data.Constants.TMBTierHexColors[TMBTier], - TMBTier + local tierString = ("|c00%s%s|r"):format( + Constants.TMBTierHexColors[tier], + tier ); - -- Add the header - tinsert(Lines, string.format( - "\n|cFF%s%s|r", - GL.Data.Constants.addonHexColor, - L.THATSMYBIS_ABBR - )); - TMBHeaderAdded = true; + -- Add the source header (TMB/DFT etc) + addSourceHeader(); -- Add the tier string tinsert(Lines, (L.TMB_TOOLTIP_TIER):format(tierString)); end -- This item has a note, show it! - if (not GL:empty(TMBNote)) then - if (not TMBHeaderAdded) then - -- Add the header - tinsert(Lines, string.format( - "\n|cFF%s%s|r", - GL.Data.Constants.addonHexColor, - L.THATSMYBIS_ABBR - )); - end + if (not GL:empty(note)) then + -- Add the source header (TMB/DFT etc) + addSourceHeader(); -- Add the note - tinsert(Lines, (L.TMB_TOOLTIP_NOTE):format(TMBNote)); + tinsert(Lines, (L.TMB_TOOLTIP_NOTE):format(note)); end end - -- No wishes defined for this item - if (GL:empty(TMBInfo)) then + -- There are no player details for this item + if (GL:empty(Entries)) then return Lines; end - local showPlayerGroups = GL:count(GL.DB:get("TMB.RaidGroups", {})) > 1 - and Settings:get("TMB.showRaidGroup"); + -- Sort the entries on a per-source basis + Entries = self:sortEntries(Entries); + + -- RRobin + if (self:wasImportedFromRRobin()) then + return self:RRobinTooltipLines(Lines, Entries); + end + + -- DFT + if (self:wasImportedFromDFT()) then + return self:DFTTooltipLines(Lines, Entries); + end + + -- CPR + if (self:wasImportedFromDFT()) then + return self:CPRTooltipLines(Lines, Entries); + end + + local showPlayerGroups = GL:count(GL.DB:get("TMB.RaidGroups", {})) > 1 and Settings:get("TMB.showRaidGroup"); local WishListEntries = {}; local PrioListEntries = {}; local itemIsOnSomeonesWishlist = false; local itemIsOnSomeonesPriolist = false; local entriesAdded = 0; - for _, Entry in pairs(TMBInfo) do - local playerName = string.lower(Entry.character); + for _, Entry in pairs(Entries) do + local playerName = Entry.character:lower(); local playerGroup = false; if (showPlayerGroups) then @@ -338,10 +459,10 @@ function TMB:tooltipLines(itemLink) local prio = Entry.prio; local entryType = Entry.type or Constants.tmbTypeWish; - local isOffSpec = GL:strContains(Entry.character, "%(os%)"); + local isOffSpec = GL:strContains(Entry.character, OFFSPEC_IDENTIFIER); local prioOffset = 0; local sortingOrder = prio; - local color = GL:classHexColor(GL.Player:classByName(playerName:gsub("%(os%)", ""), 0), GL.Data.Constants.disabledTextColor); + local color = GL:classHexColor(GL.Player:classByName(playerName:gsub(OFFSPEC_IDENTIFIER, ""), 0), Constants.disabledTextColor); -- We add 100 to the prio (first key) of the object -- This object is used for sorting later and is not visible to the player @@ -365,10 +486,10 @@ function TMB:tooltipLines(itemLink) -- TMB is not case-sensitive so people get creative with capital letters sometimes playerName = GL:capitalize(playerName); if (entryType == Constants.tmbTypePrio) then - tinsert(PrioListEntries, { sortingOrder, string.format("|cFF%s %s[%s]%s|r", color, playerName, prio, groupString) }); + tinsert(PrioListEntries, { sortingOrder, ("|c00%s %s[%s]%s|r"):format(color, playerName, prio, groupString) }); itemIsOnSomeonesPriolist = true; else - tinsert(WishListEntries, { sortingOrder, string.format("|cFF%s %s[%s]%s|r", color, playerName, prio, groupString) }); + tinsert(WishListEntries, { sortingOrder, ("|c00%s %s[%s]%s|r"):format(color, playerName, prio, groupString) }); itemIsOnSomeonesWishlist = true; end end @@ -380,7 +501,7 @@ function TMB:tooltipLines(itemLink) ) ) then -- Add the header - local source = GL.TMB:source(); + local source = self:source(); tinsert(Lines, ("\n|c00FF7A0A%s|r"):format((L.TMB_TOOLTIP_PRIO_HEADER):format(source))); PrioListEntries = self:sortEntries(PrioListEntries, 1); @@ -391,9 +512,9 @@ function TMB:tooltipLines(itemLink) entriesAdded = entriesAdded + 1; tinsert(Lines, string.format( - "|cFF%s%s|r", - GL:classHexColor(GL.Player:classByName(Entry[2], 0), GL.Data.Constants.disabledTextColor), - GL:capitalize(Entry[2]):gsub("%(os%)", " " .. L.TMB_TOOLTIP_OFFSPEC_INDICATION) + "|c00%s%s|r", + GL:classHexColor(GL.Player:classByName(Entry[2], 0), Constants.disabledTextColor), + GL:capitalize(Entry[2]):gsub(OFFSPEC_IDENTIFIER, " " .. L.TMB_TOOLTIP_OFFSPEC_INDICATION) )); -- Make sure we don't add more names to the tooltip than the user allowed @@ -427,9 +548,9 @@ function TMB:tooltipLines(itemLink) entriesAdded = entriesAdded + 1; tinsert(Lines, string.format( - "|cFF%s%s|r", - GL:classHexColor(GL.Player:classByName(Entry[2], 0), GL.Data.Constants.disabledTextColor), - GL:capitalize(Entry[2]):gsub("%(os%)", " " .. L.TMB_TOOLTIP_OFFSPEC_INDICATION) + "|c00%s%s|r", + GL:classHexColor(GL.Player:classByName(Entry[2], 0), Constants.disabledTextColor), + GL:capitalize(Entry[2]):gsub(OFFSPEC_IDENTIFIER, " " .. L.TMB_TOOLTIP_OFFSPEC_INDICATION) )); -- Make sure we don't add more names to the tooltip than the user allowed @@ -444,8 +565,7 @@ end --- Draw either the importer or overview --- based on the current TMB data ---- ----@return void +---@param source? string function TMB:draw(source) -- No data available, show importer if (not self:available()) then @@ -457,8 +577,6 @@ function TMB:draw(source) end --- Clear all TMB data ---- ----@return void function TMB:clear() GL.DB.TMB = {}; @@ -466,7 +584,6 @@ function TMB:clear() end --- Check whether the current TMB data was imported from DFT ---- ---@return boolean function TMB:wasImportedFromDFT() return self:available() and GL:toboolean(GL.DB.TMB.MetaData.importedFromDFT); @@ -479,6 +596,13 @@ function TMB:wasImportedFromCPR() return self:available() and GL:toboolean(GL.DB.TMB.MetaData.importedFromCPR); end +--- Check whether the current TMB data was imported from RRobin +--- +---@return boolean +function TMB:wasImportedFromRRobin() + return self:available() and GL:toboolean(GL.DB.TMB.MetaData.importedFromRRobin); +end + --- Check whether the current TMB data was imported from CSV --- ---@return boolean @@ -688,6 +812,7 @@ function TMB:import(data, triedToDecompress, source) importedFromDFT = GL:toboolean(WebsiteData.importedFromDFT), importedFromCSV = GL:toboolean(WebsiteData.importedFromCSV), importedFromCPR = source == "cpr", + importedFromRRobin = source == "rrobin", importedAt = GetServerTime(), hash = GL:uuid() .. GetServerTime(), }; @@ -709,10 +834,10 @@ function TMB:import(data, triedToDecompress, source) end -- Report players without any TMB entries - local PlayersWithoutTMBDetails = self:playersWithoutTMBDetails(); - if (not GL:empty(PlayersWithoutTMBDetails)) then + local PlayersWithoutEntries = self:playersWithoutEntries(); + if (not GL:empty(PlayersWithoutEntries)) then local MissingPlayers = {}; - for _, name in pairs(PlayersWithoutTMBDetails) do + for _, name in pairs(PlayersWithoutEntries) do tinsert(MissingPlayers, GL:nameFormat{ name = name, colorize = true, }); end @@ -726,15 +851,19 @@ end --- Where did our current TMB data come from? ---@return string function TMB:source() - if (GL.TMB:wasImportedFromDFT()) then + if (self:wasImportedFromDFT()) then return L.DFT; end - if (GL.TMB:wasImportedFromCPR()) then + if (self:wasImportedFromRRobin()) then + return "RRobin"; + end + + if (self:wasImportedFromCPR()) then return L.CLASSICPRIO_ABBR; end - if (GL.TMB:wasImportedFromCSV()) then + if (self:wasImportedFromCSV()) then return L.ITEM; end @@ -742,9 +871,8 @@ function TMB:source() end --- Return the names of all players that don't have any TMB details ---- ---@return table -function TMB:playersWithoutTMBDetails() +function TMB:playersWithoutEntries() local PlayersWithDetails = {}; for _, Item in pairs(GL.DB:get("TMB.Items", {})) do for _, Entry in pairs(Item or {}) do @@ -754,7 +882,7 @@ function TMB:playersWithoutTMBDetails() end end - local PlayersWithoutTMBDetails = {}; + local PlayersWithoutEntries = {}; for _, Details in pairs(GL.User:groupMembers() or {}) do local name = string.lower(GL:nameFormat(Details.name)); @@ -762,17 +890,16 @@ function TMB:playersWithoutTMBDetails() if (not PlayersWithDetails[name] and not PlayersWithDetails[fqn] ) then - tinsert(PlayersWithoutTMBDetails, name); + tinsert(PlayersWithoutEntries, name); end end - return PlayersWithoutTMBDetails; + return PlayersWithoutEntries; end --- Attempt to transform the DFT format to a TMB format ---- ---@param data string ----@return boolean|table +---@return boolean|table Returns false when invalid data is provided function TMB:DFTFormatToTMB(data) local TMBData = { importedFromDFT = true, @@ -874,9 +1001,8 @@ function TMB:DFTFormatToTMB(data) end --- Attempt to transform the Gargul CSV format to a TMB format ---- ---@param data string ----@return boolean|table +---@return boolean|table Returns false when invalid data is provided function TMB:CSVFormatToTMB(data) local TMBData = { importedFromCSV = true, @@ -959,12 +1085,12 @@ Alt:ratomir,zhorax,feth for _, priorityEntry in pairs(CSVParts) do (function () -- Not having continue statements in LUA is getting silly at this point local player = string.lower(GL:nameFormat(priorityEntry)); - local playerPriority = player:match("(%[[0-9]+%])"); + local playerPriority = player:match("(%[[0-9%.]+%])"); if (playerPriority) then local openingBracketPosition = string.find(player, "%["); player = string.sub(player, 1, openingBracketPosition - 1); - priority = playerPriority:match("([0-9]+)"); + priority = playerPriority:match("([0-9%.]+)"); elseif (string.find(player, "%|")) then local Players = GL:explode(player, "|"); @@ -1040,7 +1166,6 @@ function TMB:decompress(data) end --- Broadcast the TMB to the RAID / PARTY ---- ---@param sendEmptyPayload boolean used to override your raider's TMB data ---@return boolean function TMB:broadcast(sendEmptyPayload) @@ -1095,7 +1220,7 @@ function TMB:broadcast(sendEmptyPayload) return true; end ----@return void +--- Broadcast the available data to whitelisted players only function TMB:broadcastToWhitelist() if (self.broadcastInProgress) then GL:error(L.BROADCAST_IN_PROGRESS_ERROR); @@ -1164,7 +1289,7 @@ function TMB:broadcastToWhitelist() end); end ----@return void +--- Broadcast the available data to everyone in your raid/party function TMB:broadcastToGroup() if (self.broadcastInProgress) then GL:error(L.BROADCAST_IN_PROGRESS_ERROR); @@ -1220,7 +1345,6 @@ function TMB:broadcastToGroup() end --- Process an incoming TMB broadcast ---- ---@param CommMessage CommMessage function TMB:receiveBroadcast(CommMessage) -- No need to update our tables if we broadcasted them ourselves @@ -1269,8 +1393,6 @@ function TMB:receiveBroadcast(CommMessage) end --- Request TMB data from the person in charge (ML or Leader) ---- ----@return void function TMB:requestData() if (self.requestingData) then return; @@ -1335,9 +1457,7 @@ function TMB:requestData() end --- Reply to a player's TMB data request ---- ---@param CommMessage CommMessage ----@return void function TMB:replyToDataRequest(CommMessage) -- I don't have any data, leave me alone! if (not self:available()) then @@ -1411,13 +1531,14 @@ end ---@param Data table ---@param key number The key that holds the item order ---@return table -function TMB:sortEntries(Data, key) +function TMB:sortEntries(Entries, key) key = key or "prio"; - Data = not GL:empty(Data) and Data or {}; + Entries = not GL:empty(Entries) and Entries or {}; + Entries = GL:tableValues(Entries); - table.sort(Data, function(a, b) + table.sort(Entries, function(a, b) if (a[key] and b[key]) then - if (self:wasImportedFromDFT()) then + if (self:wasImportedFromDFT() or self:wasImportedFromRRobin()) then return a[key] > b[key]; end @@ -1427,12 +1548,182 @@ function TMB:sortEntries(Data, key) return false; end); - return Data; + return Entries; end --- Check whether the current user is allowed to broadcast TMB data ---- ---@return boolean function TMB:userIsAllowedToBroadcast() return GL.User.isInGroup and (GL.User.isMasterLooter or GL.User.hasAssist); end + +--- Announce TMB entry details in chat +---@param itemID number +---@param Entries? table +function TMB:announceDetailsOfItemInChat(itemID, Entries) + local announcePrios = GL.Settings:get("TMB.includePrioListInfoInLootAnnouncement"); + local announceWishes = GL.Settings:get("TMB.includeWishListInfoInLootAnnouncement"); + + -- It seems we're not interested in announcing anything TMB-related + if (not announcePrios + and not announceWishes + ) then + return; + end + + if (self:wasImportedFromRRobin()) then + if (not announcePrios) then + return; + end + + return self:RRobinAnnounceDetailsOfItemInChat(itemID, Entries); + end + + Entries = Entries or self:byItemID(itemID); + if (GL:empty(Entries)) then + return; + end + + local numberOfEntriesToAnnounce = GL.Settings:get("TMB.maximumNumberOfAnnouncementEntries", 5); + local WishListDetails = {}; + local PrioListDetails = {}; + for _, Entry in pairs(Entries) do + local prio = Entry.prio; + local entryType = Entry.type or Constants.tmbTypeWish; + local isOffSpec = GL:strContains(playerName, OFFSPEC_IDENTIFIER); + local sortingOffset = 0; + local sortingOrder = tonumber(prio); + + -- We add 100 to the prio (first key) of the object + -- This object is used for sorting later and is not visible to the player + if (isOffSpec) then + sortingOffset = 100; + end + + if (not GL:empty(sortingOrder)) then + sortingOrder = prio + sortingOffset; + else + -- If for whatever reason we can't determine the + -- item prio then we add it to the end of the list by default + sortingOrder = 1000; + end + + if (entryType == Constants.tmbTypePrio) then + tinsert(PrioListDetails, { + prio = sortingOrder, + player = string.format("%s[%s]", playerName, prio), + }); + else + tinsert(WishListDetails, { + prio = sortingOrder, + player = string.format("%s[%s]", playerName, prio), + }); + end + end + + local itemIsOnSomeonesPriolist = not GL:empty(PrioListDetails); + local itemIsOnSomeonesWishlist = not GL:empty(WishListDetails); + + --[[ + SHOW WHO HAS PRIORITY ON THIS ITEM (TMB) + ]] + if (itemIsOnSomeonesPriolist + and GL.Settings:get("TMB.includePrioListInfoInLootAnnouncement") + ) then + PrioListDetails = TMB:sortEntries(PrioListDetails); + + local entries = 0; + local entryString = ""; + for _, Entry in pairs(PrioListDetails) do + entries = entries + 1; + + -- Add the player to the list (first entry should not start with a comma) + if (GL:empty(entryString)) then + entryString = GL:capitalize(Entry.player); + else + entryString = entryString .. ", " .. GL:capitalize(Entry.player); + end + + -- The user only wants to see a limited number of entries, break! + if (entries >= numberOfEntriesToAnnounce) then + break; + end + end + + GL:sendChatMessage( + (L.CHAT.TMB_PRIORITY_DETAILS):format(TMB:source(), entryString), + "GROUP" + ); + end + + --[[ + SHOW WHO WISHLISTED THIS ITEM (TMB) + ]] + if (itemIsOnSomeonesWishlist + and GL.Settings:get("TMB.includeWishListInfoInLootAnnouncement") + and (not itemIsOnSomeonesPriolist + or not GL.Settings:get("TMB.hideWishListInfoIfPriorityIsPresent") + ) + ) then + WishListDetails = TMB:sortEntries(WishListDetails); + + local entries = 0; + local entryString = ""; + for _, Entry in pairs(WishListDetails) do + entries = entries + 1; + + -- Add the player to the list (first entry should not start with a comma) + if (GL:empty(entryString)) then + entryString = GL:capitalize(Entry.player); + else + entryString = entryString .. ", " .. GL:capitalize(Entry.player); + end + + -- The user only wants to see a limited number of entries, break! + if (entries >= numberOfEntriesToAnnounce) then + break; + end + end + + GL:sendChatMessage( + (L.CHAT.TMB_WISHLIST_DETAILS):format(entryString), + "GROUP" + ); + end +end + +--- Announce RRobin details in chat +---@param itemID number +---@param Entries? table +function TMB:RRobinAnnounceDetailsOfItemInChat(itemID, Entries) + Entries = Entries or self:byItemID(itemID); + if (GL:empty(Entries)) then + return; + end + Entries = self:sortEntries(Entries) + + local EligibleEntries = {}; + local entries = 0; + local topPrio = Entries[1].prio; + local numberOfEntriesToAnnounce = GL.Settings:get("TMB.maximumNumberOfAnnouncementEntries", 5); + for _, Entry in pairs(Entries) do + -- We've reached the end of all eligible entries + if (topPrio - Entry.prio > 1) then + break; + end + + tinsert(EligibleEntries, ("%s[%s]"):format(GL:disambiguateName(Entry.character), Entry.prio)); + entries = entries + 1; + + -- The user only wants to see a limited number of entries, break! + if (entries >= numberOfEntriesToAnnounce) then + break; + end + end + + local entryString = table.concat(EligibleEntries, ","); + GL:sendChatMessage( + (L.CHAT.TMB_PRIORITY_DETAILS):format(self:source(), entryString), + "GROUP" + ); +end diff --git a/Data/Constants.lua b/Data/Constants.lua index 8511c203..aeec15e9 100644 --- a/Data/Constants.lua +++ b/Data/Constants.lua @@ -5,7 +5,7 @@ local _, GL = ...; GL.Data = GL.Data or {}; ---@class Constants -GL.Data.Constants = { +local Constants = { defaultFrameTitle = string.format("Gargul |c00967FD2v%s|r", GL.version), discordURL = "https://discord.gg/D3mDhYPVzf", @@ -553,6 +553,8 @@ GL.Data.Constants = { channel = "GargulComm2", -- Due to a Blizzard issue with whisper comms, era requires a higher min version minimumAppVersion = GL.isEra and "7.3.2" or "7.2.16", + + ---@class CommActions Actions = { awardItem = 1, broadcastLootPriorities = 2, @@ -596,4 +598,5 @@ GL.Data.Constants = { ascending = 1, descending = 2, }, -}; \ No newline at end of file +}; +GL.Data.Constants = Constants; \ No newline at end of file diff --git a/Data/Localizations/chat.lua b/Data/Localizations/chat.lua index addf677b..4e024fb1 100644 --- a/Data/Localizations/chat.lua +++ b/Data/Localizations/chat.lua @@ -86,7 +86,7 @@ L.CHAT = { ROLLING_STOP = "Stop your rolls!", ROLLING_TIME_LEFT = "%s seconds to roll", ROLLING_SOFTRES_INFO = "This item was reserved by: %s", - ROLLING_TMB_INFO = "The following players have the highest %s prio: %s", -- First %s can be TMB/DFT/CPR + ROLLING_TMB_INFO = "These players have the highest %s prio: %s", -- First %s can be TMB/DFT/CPR --[[ TradeWindow / Trade ]] TRADE_TO = "%s to %s", diff --git a/Data/Localizations/cn.lua b/Data/Localizations/cn.lua index 1ceba9d2..30cf96bb 100644 --- a/Data/Localizations/cn.lua +++ b/Data/Localizations/cn.lua @@ -924,7 +924,7 @@ L.TMB_IMPORT_INVALID_DFT = "提供的 DFT 数据无效,请按照工作表的 L.TMB_IMPORT_INVALID_INSTRUCTIONS = "提供的 TMB 数据无效,请务必单击 Gargul 部分中的“下载”按钮并将内容按原样粘贴到此处!"; L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "提供的 TMB 或 DFT 数据无效,请确保将导出内容按原样粘贴到此处!"; L.TMB_IMPORT_PLAYER_NO_DATA = "以下玩家没有 %s 条目:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "如何将 Gargul 与 TMB 结合使用"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "如何将 Gargul 与 ${source} 结合使用"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "将 TMB 导出内容按原样粘贴到下面的框中,然后单击“导入”"; L.TMB_NO_BROADCAST_TARGETS = "您的群组中没有人可以向其广播"; diff --git a/Data/Localizations/de.lua b/Data/Localizations/de.lua index c7589b3b..b7b8e2a8 100644 --- a/Data/Localizations/de.lua +++ b/Data/Localizations/de.lua @@ -934,7 +934,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Es wurden ungültige TMB- oder DFT- L.TMB_IMPORT_NOTES_AVAILABLE = "Prioritätsnotizen verfügbar: %s"; L.TMB_IMPORT_NUMBER = "Anzahl der importierten Gegenstände: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "Die folgenden Spieler haben keine %s Einträge:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "So verwendest du Gargul mit TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "So verwendest du Gargul mit ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Füge den Inhalt deines TMB-Exports unverändert in das Feld unten ein und klick auf „Importieren“."; L.TMB_NO_BROADCAST_TARGETS = "Es gibt niemanden in deiner Gruppe, an den du senden können"; diff --git a/Data/Localizations/en.lua b/Data/Localizations/en.lua index ced9961d..09e8a4a3 100644 --- a/Data/Localizations/en.lua +++ b/Data/Localizations/en.lua @@ -937,7 +937,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Invalid TMB or DFT data provided, m L.TMB_IMPORT_NOTES_AVAILABLE = "Priority notes available: %s"; L.TMB_IMPORT_NUMBER = "Number of items imported: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "The following players have no %s entries:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "How to use Gargul with TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "How to use Gargul with ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Paste your TMB export contents as-is in the box below and click 'Import'"; L.TMB_NO_BROADCAST_TARGETS = "There's no one in your group to broadcast to"; diff --git a/Data/Localizations/es.lua b/Data/Localizations/es.lua index 884dad8e..f601e248 100644 --- a/Data/Localizations/es.lua +++ b/Data/Localizations/es.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Se proporcionaron datos TMB o DFT n L.TMB_IMPORT_NOTES_AVAILABLE = "Notas prioritarias disponibles: %s"; L.TMB_IMPORT_NUMBER = "Número de artículos importados: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "Los siguientes jugadores no tienen %s entradas:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "Cómo utilizar Gargul con TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "Cómo utilizar Gargul con ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Pegue el contenido de exportación de TMB tal como está en el cuadro a continuación y haga clic en 'Importar'"; L.TMB_NO_BROADCAST_TARGETS = "No hay nadie en tu grupo para transmitir"; diff --git a/Data/Localizations/fr.lua b/Data/Localizations/fr.lua index 66203b26..417bb098 100644 --- a/Data/Localizations/fr.lua +++ b/Data/Localizations/fr.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Données TMB ou DFT fournies non va L.TMB_IMPORT_NOTES_AVAILABLE = "Notes de priorité disponibles : %s"; L.TMB_IMPORT_NUMBER = "Nombre d'objets importés : %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "Les joueurs suivants n'ont pas d'entrée %s :"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "Comment utiliser Gargul avec TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "Comment utiliser Gargul avec ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Collez le contenu de votre export TMB tel quel dans la case ci-dessous et cliquez sur 'Importer'."; L.TMB_NO_BROADCAST_TARGETS = "Il n'y a personne dans votre groupe vers qui diffuser"; diff --git a/Data/Localizations/it.lua b/Data/Localizations/it.lua index f24ebac7..96a086e3 100644 --- a/Data/Localizations/it.lua +++ b/Data/Localizations/it.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Dati TMB o DFT forniti non validi, L.TMB_IMPORT_NOTES_AVAILABLE = "Note prioritarie disponibili: %s"; L.TMB_IMPORT_NUMBER = "Numero di elementi importati: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "I seguenti giocatori non hanno voci %s:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "Come usare Gargul con TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "Come usare Gargul con ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Incolla il contenuto dell'esportazione TMB così com'è nella casella sottostante e fai clic su 'Importa'"; L.TMB_NO_BROADCAST_TARGETS = "Non c'è nessuno nel tuo gruppo a cui trasmettere"; diff --git a/Data/Localizations/ko.lua b/Data/Localizations/ko.lua index f3c12037..b3e380b8 100644 --- a/Data/Localizations/ko.lua +++ b/Data/Localizations/ko.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "잘못된 TMB 또는 DFT 데이터 L.TMB_IMPORT_NOTES_AVAILABLE = "사용 가능한 우선순위 메모: %s"; L.TMB_IMPORT_NUMBER = "가져온 항목 수: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "다음 플레이어에게는 %s 항목이 없습니다:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "TMB와 함께 Gargul을 사용하는 방법"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "${source}와 함께 Gargul을 사용하는 방법"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "TMB 내보내기 내용을 그대로 아래 상자에 붙여넣고 '가져오기'를 클릭하세요."; L.TMB_NO_BROADCAST_TARGETS = "그룹에 방송할 사람이 없습니다."; diff --git a/Data/Localizations/pt.lua b/Data/Localizations/pt.lua index 186a0a26..c2c8de0d 100644 --- a/Data/Localizations/pt.lua +++ b/Data/Localizations/pt.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Dados TMB ou DFT inválidos forneci L.TMB_IMPORT_NOTES_AVAILABLE = "Notas prioritárias disponíveis: %s"; L.TMB_IMPORT_NUMBER = "Número de itens importados: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "Os seguintes jogadores não têm %s entradas:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "Como usar Gargul com TMB"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "Como usar Gargul com ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Cole o conteúdo de exportação do TMB como está na caixa abaixo e clique em 'Importar'"; L.TMB_NO_BROADCAST_TARGETS = "Não há ninguém no seu grupo para quem transmitir"; diff --git a/Data/Localizations/ru.lua b/Data/Localizations/ru.lua index 8b40462e..5db811da 100644 --- a/Data/Localizations/ru.lua +++ b/Data/Localizations/ru.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "Предоставлены неве L.TMB_IMPORT_NOTES_AVAILABLE = "Доступны приоритетные примечания: %s"; L.TMB_IMPORT_NUMBER = "Количество импортированных товаров: %s"; L.TMB_IMPORT_PLAYER_NO_DATA = "У следующих игроков нет записей %s:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "Как использовать Гаргул с ТМБ"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "Как использовать Гаргул с ${source}"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "Вставьте содержимое экспорта TMB как есть в поле ниже и нажмите «Импортировать»."; L.TMB_NO_BROADCAST_TARGETS = "В вашей группе нет никого, кому можно было бы транслировать"; diff --git a/Data/Localizations/tw.lua b/Data/Localizations/tw.lua index 01af8919..1b6c38f7 100644 --- a/Data/Localizations/tw.lua +++ b/Data/Localizations/tw.lua @@ -935,7 +935,7 @@ L.TMB_IMPORT_INVALID_UNKNOWN_INSTRUCTIONS = "提供的 TMB 或 DFT 資料無效 L.TMB_IMPORT_NOTES_AVAILABLE = "可用的優先註釋:%s"; L.TMB_IMPORT_NUMBER = "導入的項目數:%s"; L.TMB_IMPORT_PLAYER_NO_DATA = "以下玩家沒有 %s 條目:"; -- %s can be TMB/DFT/CPR -L.TMB_IMPORT_TMB_GARGUL_INFO = "如何將 Gargul 與 TMB 結合使用"; +L.TMB_IMPORT_TMB_GARGUL_INFO = "如何將 Gargul 與 ${source} 結合使用"; L.TMB_IMPORT_TMB_GARGUL_INFO_URL = "https://github.com/papa-smurf/Gargul/wiki/Gargul-and-ThatsMyBIS"; L.TMB_IMPORT_TMB_INFO = "將 TMB 匯出內容按原樣貼上到下面的框中,然後按一下“匯入”"; L.TMB_NO_BROADCAST_TARGETS = "您的群組中沒有人可以向其廣播"; diff --git a/Interface/TMB/Importer.lua b/Interface/TMB/Importer.lua index 0d307c7e..71d43cfe 100644 --- a/Interface/TMB/Importer.lua +++ b/Interface/TMB/Importer.lua @@ -66,7 +66,7 @@ function Importer:draw(source) Window:AddChild(VerticalSpacer); local MoreInfoLabel = GL.AceGUI:Create("Label"); - MoreInfoLabel:SetText(("|c00FFF569%s|r"):format(L.TMB_IMPORT_TMB_GARGUL_INFO)); + MoreInfoLabel:SetText(("|c00FFF569%s|r"):format(GL:printfn(L.TMB_IMPORT_TMB_GARGUL_INFO, { source = GL.TMB:source(), }))); MoreInfoLabel:SetFontObject(_G["GameFontGreenLarge"]); MoreInfoLabel:SetFullWidth(true); MoreInfoLabel:SetJustifyH("CENTER"); diff --git a/Interface/TMB/Overview.lua b/Interface/TMB/Overview.lua index 68726dd5..0b695ede 100644 --- a/Interface/TMB/Overview.lua +++ b/Interface/TMB/Overview.lua @@ -59,7 +59,7 @@ function Overview:draw() Window:AddChild(VerticalSpacer); local MoreInfoLabel = GL.AceGUI:Create("Label"); - MoreInfoLabel:SetText(L.TMB_IMPORT_TMB_GARGUL_INFO); + MoreInfoLabel:SetText(GL:printfn(L.TMB_IMPORT_TMB_GARGUL_INFO, { source = GL.TMB:source(), })); MoreInfoLabel:SetFontObject(_G["GameFontGreenLarge"]); MoreInfoLabel:SetFullWidth(true); MoreInfoLabel:SetJustifyH("CENTER"); diff --git a/bootstrap.lua b/bootstrap.lua index dbb40a15..b490e24f 100644 --- a/bootstrap.lua +++ b/bootstrap.lua @@ -65,10 +65,10 @@ function GL:bootstrap(_, _, addonName) GL:after(1, nil, function() -- Check if ElvUI is loaded (useful for making adhoc UI changes) - self.elvUILoaded = GetAddOnEnableState(GL.User.name,"ElvUI") == 2; + self.elvUILoaded = C_AddOns.GetAddOnEnableState(GL.User.name,"ElvUI") == 2; -- Check if the user doesn't already have MuteNotInGroup loaded - if (self.isClassic and GetAddOnEnableState(GL.User.name, "MuteNotInGroup") ~= 2) then + if (self.isClassic and C_AddOns.GetAddOnEnableState(GL.User.name, "MuteNotInGroup") ~= 2) then -- Ignore "You aren't in a party" messages when you are in fact in a party (Blizzard LFD Bug) ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", function (_, _, msg) return msg == ERR_NOT_IN_GROUP