You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
13 KiB

--[[
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
]]--