--[[ Name: LibAlts-1.0 Revision: 50 Author: Sylvanaar (sylvanaar@mindspring.com) Description: Shared handling of alt identity between addons. Dependencies: LibStub License: ]]-- local MAJOR, MINOR = "LibAlts-1.0", 1 local LibStub = LibStub local lib = LibStub:NewLibrary(MAJOR, MINOR) if not lib then return end local _G = getfenv(0) lib.Alts = lib.Alts or {} lib.AltsPerSource = lib.AltsPerSource or {} lib.RealIds = lib.RealIds or {} local Alts = lib.Alts local AltsPerSource = lib.AltsPerSource local Mains -- reverse lookup table local MainsPerSource = {} -- reverse lookup table --local RealIds = lib.RealIds --local RealIdMains = {} -- reverse lookup table local tinsert = _G.tinsert local unpack = _G.unpack local pairs = _G.pairs local tremove = _G.tremove local tsort = _G.table.sort local tContains = _G.tContains local ipairs = _G.ipairs local wipe = _G.wipe lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib) local callbacks = lib.callbacks -- Regexp to match the first character for UTF8 strings local MULTIBYTE_FIRST_CHAR = "^([\192-\255]?%a?[\128-\191]*)" -- Constants for the source prefixes. These should be prepended for guild or -- addon source names so they remain consistent. The guild name itself should -- be the exact value from the Blizzard API. Addons should use their exact name. lib.GUILD_PREFIX = "guild:" lib.ADDON_PREFIX = "addon:" local function reverseTable(table) local reverse = {} if table then for k,v in pairs(table) do for i,a in ipairs(v) do reverse[a] = k end end end return reverse end --- Register a Main-Alt relationship. -- User-entered data should not have a source (i.e, source = nil). For guild data, -- prepend "guild:" to the guild name returned from the API. Addons -- wishing to add their own separate data should prepend "addon:". Any data not -- directly entered by a user should be added as a source. -- @name :SetAlt -- @param main Name of the main character. -- @param alt Name of the alt character. -- @param source Source of the main-alt relationship. Nil if used-defined. function lib:SetAlt(main, alt, source) if (not main) or (not alt) then return end main = self:TitleCase(main) alt = self:TitleCase(alt) if not source then -- Adding the relationship to the user-defined data. Mains = nil Alts[main] = Alts[main] or {} for i,v in ipairs(Alts[main]) do if v == alt then return end end tinsert(Alts[main], alt) else -- Adding a relationship for a specific source. MainsPerSource[source] = nil AltsPerSource[source] = AltsPerSource[source] or {} AltsPerSource[source][main] = AltsPerSource[source][main] or {} for i,v in ipairs(AltsPerSource[source][main]) do if v == alt then return end end tinsert(AltsPerSource[source][main], alt) end callbacks:Fire("LibAlts_SetAlt", main, alt, source) end --- Get a list of alts for a given character. -- @name :GetAlts -- @param main Name of the main character. -- @return list list of alts. function lib:GetAlts(main) if not main then return nil end main = self:TitleCase(main) local alts = {} if Alts[main] and #Alts[main] > 0 then for i, v in ipairs(Alts[main]) do if not tContains(alts, v) then tinsert(alts, v) end end end for k, v in pairs(AltsPerSource) do if AltsPerSource[k][main] and #AltsPerSource[k][main] > 0 then for i, v in ipairs(AltsPerSource[k][main]) do if not tContains(alts, v) then tinsert(alts, v) end end end end if #alts > 0 then return unpack(alts) end return nil end --- Get a list of alts for a given character for a given data source. -- @name :GetAltsForSource -- @param main Name of the main character. -- @param source The data source to use. Nil for user-defined. -- @return list list of alts. function lib:GetAltsForSource(main, source) if not main then return end main = self:TitleCase(main) if not source then if Alts[main] and #Alts[main] > 0 then return unpack(Alts[main]) end else if not AltsPerSource[source] then return nil end if AltsPerSource[source][main] and #AltsPerSource[source][main] > 0 then return unpack(AltsPerSource[source][main]) end end return nil end --- Get the main for a given alt character. -- @name :GetMain -- @param alt Name of the alt character. -- @return string the main character. function lib:GetMain(alt) if not alt then return end alt = self:TitleCase(alt) if not Mains then Mains = reverseTable(Alts) end -- Check the user-defined data first. local main = Mains[alt] if main then return main end -- Check the various data sources. for k, v in pairs(AltsPerSource) do -- Check that the reverse table is built for the data source if not MainsPerSource[k] then MainsPerSource[k] = reverseTable(AltsPerSource[k]) end main = MainsPerSource[k][alt] if main then return main end end return nil end --- Get the main for a given alt character for a given data source. -- @name :GetMainForSource -- @param alt Name of the alt character. -- @param source The data source to use. -- @return string the main character. function lib:GetMainForSource(alt, source) if not alt then return nil end alt = self:TitleCase(alt) if not source then if not Mains then Mains = reverseTable(Alts) end return Mains[alt] else if not AltsPerSource[source] then return nil end if not MainsPerSource[source] then MainsPerSource[source] = reverseTable(AltsPerSource[source]) end return MainsPerSource[source][alt] end end --- Return a table of all main characters. -- @name :GetAllMains -- @param mains The table to fill with the mains. function lib:GetAllMains(mains) if not mains then return end for k, v in pairs(Alts) do if not tContains(mains, k) then tinsert(mains, k) end end for k, v in pairs(AltsPerSource) do for key, val in pairs(AltsPerSource[k]) do if not tContains(mains, key) then tinsert(mains, key) end end end return mains end --- Return a table of all main characters for a given data source. -- @name :GetAllMainsForSource -- @param mains The table to fill with the mains. -- @param source The data source to limit the mains for. Nil for user-defined. function lib:GetAllMainsForSource(mains, source) if not mains then return end if not source then for k, v in pairs(Alts) do tinsert(mains, k) end else if not AltsPerSource[source] then return nil end for k, v in pairs(AltsPerSource[source]) do tinsert(mains, k) end end return mains end --- Test if a character is a main. -- @name :IsMain -- @param main Name of the character. -- @return boolean is this a main character. function lib:IsMain(main) if not main then return nil end main = self:TitleCase(main) if Alts[main] then return true end for k, v in pairs(AltsPerSource) do if AltsPerSource[k][main] then return true end end return false end --- Test if a character is a main for a given data source. -- @name :IsMainForSource -- @param main Name of the character. -- @param source The data source to use. Nil for user-defined. -- @return boolean is this a main character. function lib:IsMainForSource(main, source) if not main then return nil end main = self:TitleCase(main) if not source then return Alts[main] and true or false else if not AltsPerSource[source] then return false end return AltsPerSource[source][main] and true or false end end --- Test if a character is an alt. -- @name :IsAlt -- @param alt Name of the character. -- @return boolean is this a alt character. function lib:IsAlt(alt) if not alt then return nil end alt = self:TitleCase(alt) if not Mains then Mains = reverseTable(Alts) end if Mains[alt] then return true end for k, v in pairs(AltsPerSource) do if not MainsPerSource[k] then MainsPerSource[k] = reverseTable(AltsPerSource[k]) end if MainsPerSource[k][alt] then return true end end return false end --- Test if a character is an alt for a given data source. -- @name :IsAltForSource -- @param alt Name of the character. -- @param source The data source to use. Nil for user-defined. -- @return boolean is this a alt character. function lib:IsAltForSource(alt, source) if not alt then return nil end alt = self:TitleCase(alt) if not source then if not Mains then Mains = reverseTable(Alts) end return Mains[alt] and true or false else if not AltsPerSource[source] then return false end if not MainsPerSource[source] then MainsPerSource[source] = reverseTable(AltsPerSource[source]) end return MainsPerSource[source][alt] and true or false end end --- Remove a Main-Alt relationship and fire a callback for the disassociation. -- @name :DeleteAlt -- @param main Name of the Main character. -- @param alt Name of the Alt being removed. -- @param source Source of the main-alt relationship. Nil if used-defined. function lib:DeleteAlt(main, alt, source) main, alt = self:TitleCase(main), self:TitleCase(alt) if not source then if not Alts[main] then return end Mains = nil for i = 1, #Alts[main] do if Alts[main][i] == alt then tremove(Alts[main], i) end end if #Alts[main] == 0 then Alts[main] = nil end else if not AltsPerSource[source] or not AltsPerSource[source][main] then return end MainsPerSource[source] = nil for i = 1, #AltsPerSource[source][main] do if AltsPerSource[source][main][i] == alt then tremove(AltsPerSource[source][main], i) end end if #AltsPerSource[source][main] == 0 then AltsPerSource[source][main] = nil end end callbacks:Fire("LibAlts_RemoveAlt", main, alt, source) --Alt is the one removed end --- Remove a data source, including all main-alt relationships, and fire a callback. -- @name :RemoveSource -- @param source Data source to be removed. function lib:RemoveSource(source) if AltsPerSource[source] then wipe(AltsPerSource[source]) AltsPerSource[source] = nil end if MainsPerSource[source] then wipe(MainsPerSource[source]) MainsPerSource[source] = nil end callbacks:Fire("LibAlts_RemoveSource", source) end --- Returns a name formatted in title case (i.e., first character upper case, the rest lower). -- @name :TitleCase -- @param name The name to be converted. -- @return string The converted name. function lib:TitleCase(name) if not name then return "" end if #name == 0 then return "" end name = name:lower() return name:gsub(MULTIBYTE_FIRST_CHAR, string.upper, 1) end --[[ --- Get the main for a given Real Id name. -- @name :GetMainForRealId -- @param realid The Real Id name. -- @return string The main character name, if a mapping exists. function lib:GetMainForRealId(realid) if not realid then return nil end realid = self:TitleCase(realid) return RealIds[realid] end --- Get the Real ID name for a given main character. -- @name :GetRealIdForMain -- @param main Name of the main character. -- @return string The Real Id name for the main, if a mapping exists. function lib:GetRealIdForMain(main) if not main then return nil end main = self:TitleCase(main) return RealIdMains[main] end --- Register a Real Id-Main relationship. -- @name :SetRealIdForMain -- @param realid The Real Id name. -- @param main Name of the main character. function lib:SetRealIdForMain(realid, main) if not realid or or #realid == 0 or not main or #main == 0 then return nil end main = self:TitleCase(main) realid = self:TitleCase(realid) -- Add the forward and reverse mappings RealIds[realid] = main RealIdMains[main] = realid callbacks:Fire("LibAlts_SetRealIdForMain", realid, main) end --- Test if a Real Id-Main relationship exists for a given Real Id name. -- @name :IsRealId -- @param realid The Real Id name. -- @return boolean Does the Real Id-Main relationship exist. function lib:IsRealId(realid) if not realid then return nil end return RealIds[realid] and true or false end ]]--