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.
521 lines
15 KiB
521 lines
15 KiB
--[[
|
|
Generic Set handling functions
|
|
]]
|
|
|
|
local _,Internal = ...
|
|
local L = Internal.L
|
|
|
|
local function GetNextSetID(sets)
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
local nextID = sets.nextID or 1
|
|
while sets[nextID] ~= nil do
|
|
nextID = nextID + 1
|
|
end
|
|
sets.nextID = nextID
|
|
return nextID;
|
|
end
|
|
local function GetSet(sets, id)
|
|
if type(id) == "table" then
|
|
return id
|
|
end
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
return sets[id]
|
|
end
|
|
local function GetSetsByName(tbl, sets, name)
|
|
name = name:lower():trim()
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" and set.name:lower():trim() == name then
|
|
tbl[#tbl+1] = set;
|
|
end
|
|
end
|
|
return tbl
|
|
end
|
|
local GetSetByName
|
|
do
|
|
local comparisons = {}
|
|
function GetSetByName(sets, name, validCallback)
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
if validCallback then
|
|
local sets = GetSetsByName({}, sets, name)
|
|
|
|
wipe(comparisons)
|
|
for _,set in ipairs(sets) do
|
|
local valid, validForClass, validForSpec = validCallback(set)
|
|
comparisons[set] = (valid and 1 or 0) + (validForClass and 1 or 0) + (validForSpec and 1 or 0)
|
|
end
|
|
|
|
sort(sets, function (a,b)
|
|
if comparisons[a] == comparisons[b] then
|
|
-- Would do name, but they all have the same name, and this should be the most consistent
|
|
return a.setID < b.setID
|
|
end
|
|
|
|
return comparisons[a] > comparisons[b]
|
|
end)
|
|
|
|
return sets[1]
|
|
else -- When we cant compare the validity of the sets we just return the first one we encounter
|
|
name = name:lower():trim()
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" and set.name:lower():trim() == name then
|
|
return set;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local function GetSetByValue(sets, value, callback)
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" and callback(set, value) then
|
|
return set;
|
|
end
|
|
end
|
|
end
|
|
local function AddSet(sets, set)
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
if not set.setID then
|
|
set.setID = GetNextSetID(sets)
|
|
end
|
|
set.useCount = set.useCount or 0
|
|
sets[set.setID] = set
|
|
return set
|
|
end
|
|
local function DeleteSet(sets, id)
|
|
if type(sets) == "string" then
|
|
sets = BtWLoadoutsSets[sets]
|
|
end
|
|
|
|
if type(id) == "table" then
|
|
if id.setID then
|
|
DeleteSet(sets, id.setID);
|
|
else
|
|
for k,v in pairs(sets) do
|
|
if v == id then
|
|
sets[k] = nil;
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
else
|
|
sets[id] = nil;
|
|
if sets.nextID == nil or id < sets.nextID then
|
|
sets.nextID = id;
|
|
end
|
|
end
|
|
end
|
|
Internal.GetNextSetID = GetNextSetID;
|
|
Internal.GetSet = GetSet;
|
|
Internal.GetSetByName = GetSetByName;
|
|
Internal.GetSetsByName = GetSetsByName;
|
|
Internal.GetSetByValue = GetSetByValue;
|
|
Internal.AddSet = AddSet;
|
|
Internal.DeleteSet = DeleteSet;
|
|
|
|
--[[ Loadouts tab generic drop handler ]]
|
|
local function SetDropDownInit(self, set, index, segment, tab)
|
|
self:SetSelected(index and set[segment][index] or nil)
|
|
self:SetSets(BtWLoadoutsSets[segment])
|
|
self:SetFilters(BtWLoadoutsFilters[segment])
|
|
self:SetCategories(BtWLoadoutsCategories[segment])
|
|
self:SetIncludeNone(index ~= nil)
|
|
self:OnItemClick(function (self, setID)
|
|
local index = index or (#set[segment] + 1)
|
|
|
|
if set[segment][index] then
|
|
local subset = Internal.GetLoadoutSegment(segment).get(set[segment][index]);
|
|
subset.useCount = (subset.useCount or 1) - 1;
|
|
end
|
|
|
|
if setID == "none" then
|
|
table.remove(set[segment], index);
|
|
elseif setID == "new" then
|
|
local subset = Internal.GetLoadoutSegment(segment).add();
|
|
set[segment][index] = subset.setID;
|
|
|
|
tab.set = subset;
|
|
PanelTemplates_SetTab(BtWLoadoutsFrame, tab:GetID());
|
|
|
|
BtWLoadoutsHelpTipFlags["TUTORIAL_CREATE_TALENT_SET"] = true;
|
|
else -- Change to a specific set
|
|
set[segment][index] = setID;
|
|
end
|
|
|
|
if set[segment][index] then
|
|
local subset = Internal.GetLoadoutSegment(segment).get(set[segment][index]);
|
|
subset.useCount = (subset.useCount or 0) + 1;
|
|
end
|
|
|
|
self:SetSelected(set[segment][index])
|
|
|
|
CloseDropDownMenus()
|
|
BtWLoadoutsFrame:Update();
|
|
end)
|
|
end
|
|
Internal.SetDropDownInit = SetDropDownInit
|
|
|
|
--[[ Sets filtering functions ]]
|
|
|
|
local function FilterSetsBySearch(result, query, sets)
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" then
|
|
if query == nil or set.name:lower():find(query) ~= nil then
|
|
result[#result+1] = set;
|
|
end
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
local function ContainsOrMatches(tbl, value)
|
|
if type(tbl) == "table" then
|
|
for _,v in pairs(tbl) do
|
|
if v == value then
|
|
return true
|
|
end
|
|
end
|
|
elseif (tbl or 0) == value then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
local function FiltersMatch(filters, setFilters)
|
|
for filter,value in pairs(filters) do
|
|
if not ContainsOrMatches(setFilters[filter], value) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
local function FilterSets(result, filters, sets)
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" and FiltersMatch(filters, set.filters) then
|
|
result[#result+1] = set;
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
local function OrganiseSetsByFilter(result, sets, filter)
|
|
if filter == nil then
|
|
for _,set in pairs(sets) do
|
|
if type(set) == "table" then
|
|
result[#result+1] = set;
|
|
end
|
|
end
|
|
else
|
|
for setID,set in pairs(sets) do
|
|
if type(set) == "table" then
|
|
local value = set.filters and set.filters[filter] or 0
|
|
if type(value) == "table" then
|
|
for _,v in pairs(value) do
|
|
result[v] = result[v] or {};
|
|
result[v][#result[v] + 1] = set;
|
|
end
|
|
else
|
|
result[value] = result[value] or {};
|
|
result[value][#result[value] + 1] = set;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
--[[
|
|
... is a list of filters, eg spec, role, class, character, instanceType, etc.
|
|
]]
|
|
local function CategoriesSets(sets, ...)
|
|
local tbl = OrganiseSetsByFilter({}, sets, ...)
|
|
if select('#', ...) > 1 then
|
|
for k,v in pairs(tbl) do
|
|
tbl[k] = CategoriesSets(v, select(2, ...))
|
|
end
|
|
end
|
|
return tbl
|
|
end
|
|
local function SortSets(o)
|
|
local function padnum(d)
|
|
local dec, n = string.match(d, "(%.?)0*(.+)")
|
|
return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n)
|
|
end
|
|
table.sort(o, function(a,b)
|
|
if (a.order or 0) == (b.order or 0) then
|
|
return tostring(a.name):gsub("%.?%d+", padnum)..("%3d"):format(#b.name)
|
|
< tostring(b.name):gsub("%.?%d+", padnum)..("%3d"):format(#a.name)
|
|
end
|
|
return (a.order or 0) < (b.order or 0)
|
|
end)
|
|
return o
|
|
end
|
|
|
|
Internal.FilterSetsBySearch = FilterSetsBySearch
|
|
Internal.FilterSets = FilterSets
|
|
Internal.CategoriesSets = CategoriesSets
|
|
Internal.SortSets = SortSets
|
|
|
|
--[[ Filter Enumerators ]]
|
|
--[[
|
|
All filter enumerators take 3 params:
|
|
@limitations {table} that are used to filter items
|
|
@includeLimitations {boolean} if the filtered items should be returned but flagged as filtered
|
|
@includeOther {boolean} If an extra item on the end (called other) should be returned
|
|
@return {function} {table} {startIndex} ipairs style
|
|
]]
|
|
--
|
|
local races = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 22, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37}
|
|
local classes = {}
|
|
local specializations = {}
|
|
do -- Build Spec List
|
|
local className, classFile, classID = UnitClass("player")
|
|
|
|
classes[#classes+1] = classID
|
|
|
|
for specIndex=1,GetNumSpecializationsForClassID(classID) do
|
|
specializations[#specializations+1] = (GetSpecializationInfoForClassID(classID, specIndex))
|
|
end
|
|
|
|
local playerClassID = classID;
|
|
for classIndex=1,GetNumClasses() do
|
|
if classIndex ~= playerClassID and GetNumSpecializationsForClassID(classID) > 0 then
|
|
classes[#classes+1] = classIndex
|
|
|
|
local _, _, classID = GetClassInfo(classIndex)
|
|
for specIndex=1,GetNumSpecializationsForClassID(classID) do
|
|
local specID = GetSpecializationInfoForClassID(classID, specIndex);
|
|
specializations[#specializations+1] = specID
|
|
end
|
|
end
|
|
end
|
|
|
|
function Internal.SortClassesByName()
|
|
table.sort(classes, function (a, b)
|
|
if a == playerClassID and b ~= playerClassID then
|
|
return true
|
|
elseif b == playerClassID and a ~= playerClassID then
|
|
return false
|
|
end
|
|
return C_CreatureInfo.GetClassInfo(a).className < C_CreatureInfo.GetClassInfo(b).className
|
|
end)
|
|
|
|
wipe(specializations)
|
|
for _,classID in ipairs(classes) do
|
|
for specIndex=1,GetNumSpecializationsForClassID(classID) do
|
|
local specID = GetSpecializationInfoForClassID(classID, specIndex);
|
|
specializations[#specializations+1] = specID
|
|
end
|
|
end
|
|
end
|
|
function Internal.SortClassesByID()
|
|
table.sort(classes, function (a, b)
|
|
if a == playerClassID and b ~= playerClassID then
|
|
return true
|
|
elseif b == playerClassID and a ~= playerClassID then
|
|
return false
|
|
end
|
|
return a < b
|
|
end)
|
|
|
|
wipe(specializations)
|
|
for _,classID in ipairs(classes) do
|
|
for specIndex=1,GetNumSpecializationsForClassID(classID) do
|
|
local specID = GetSpecializationInfoForClassID(classID, specIndex);
|
|
specializations[#specializations+1] = specID
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local instanceTypeEnumeratorList = {
|
|
{ "party", L["Dungeon"], },
|
|
{ "raid", L["Raid"], }, { "arena", L["Arena"], },
|
|
{ "pvp", L["Battleground"], },
|
|
{ "scenario", L["Scenarios"], },
|
|
{ "none", L["World"], }
|
|
}
|
|
local disabledEnumeratorList = {
|
|
{ 0, L["Enabled"], },
|
|
{ 1, L["Disabled"], },
|
|
}
|
|
local roleEnumertorList = {}
|
|
do -- Build role list
|
|
for _,role in Internal.Roles() do
|
|
roleEnumertorList[#roleEnumertorList+1] = { role, _G[role] }
|
|
end
|
|
end
|
|
local charaterEnumertorList = {} -- Reused for building character lists
|
|
Internal.Filters = {
|
|
covenant = {
|
|
name = L["Covenant"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
local covenantData = C_Covenants.GetCovenantData(tbl[index])
|
|
return index, tbl[index], COVENANT_COLORS[covenantData.textureKit]:WrapTextInColorCode(covenantData.name)
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, {1, 2, 3, 4}, 0
|
|
end,
|
|
},
|
|
race = {
|
|
name = L["Race"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
return index, tbl[index], GetFactionColor(C_CreatureInfo.GetFactionInfo(tbl[index]).groupTag):WrapTextInColorCode(C_CreatureInfo.GetRaceInfo(tbl[index]).raceName)
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, races, 0
|
|
end,
|
|
},
|
|
class = {
|
|
name = L["Class"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
local classInfo = C_CreatureInfo.GetClassInfo(tbl[index])
|
|
local classColor = C_ClassColor.GetClassColor(classInfo.classFile)
|
|
return index, classInfo.classFile, classColor:WrapTextInColorCode(classInfo.className)
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, classes, 0
|
|
end,
|
|
},
|
|
spec = {
|
|
name = L["Specialization"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
local limitRole, limitClassFile
|
|
if limitations then
|
|
limitRole = limitations.role
|
|
if limitations.character then
|
|
local characterData = Internal.GetCharacterInfo(limitations.character)
|
|
limitClassFile = characterData.class
|
|
elseif limitations.class then
|
|
local classData = C_CreatureInfo.GetClassInfo(limitations.class)
|
|
limitClassFile = classData.classFile
|
|
end
|
|
end
|
|
|
|
return function (tbl, index)
|
|
repeat
|
|
index = index + 1
|
|
until not tbl[index] or includeLimitations or (
|
|
(limitClassFile == nil or limitClassFile == (select(6, GetSpecializationInfoByID(tbl[index])))) and
|
|
(limitRole == nil or limitRole == (select(5, GetSpecializationInfoByID(tbl[index]))))
|
|
)
|
|
|
|
if tbl[index] then
|
|
local _, specName, _, _, role, classFile, className = GetSpecializationInfoByID(tbl[index])
|
|
local classColor = C_ClassColor.GetClassColor(classFile);
|
|
local name = format("%s - %s", classColor:WrapTextInColorCode(className), specName)
|
|
|
|
return index, tbl[index], name, not (
|
|
(limitClassFile == nil or limitClassFile == classFile) and
|
|
(limitRole == nil or limitRole == role)
|
|
)
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, specializations, 0
|
|
end,
|
|
},
|
|
role = {
|
|
name = L["Role"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
return index, unpack(tbl[index])
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, roleEnumertorList, 0
|
|
end,
|
|
},
|
|
character = {
|
|
name = L["Character"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
wipe(charaterEnumertorList)
|
|
|
|
local name = UnitName("player")
|
|
local character = Internal.GetCharacterSlug();
|
|
local characterInfo = Internal.GetCharacterInfo(character);
|
|
if characterInfo then
|
|
local classColor = C_ClassColor.GetClassColor(characterInfo.class);
|
|
name = format("%s - %s", classColor:WrapTextInColorCode(characterInfo.name), characterInfo.realm);
|
|
end
|
|
charaterEnumertorList[#charaterEnumertorList+1] = {character, name}
|
|
|
|
local playerCharacter = character
|
|
for _,character in Internal.CharacterIterator() do
|
|
if playerCharacter ~= character then
|
|
local characterInfo = Internal.GetCharacterInfo(character);
|
|
if characterInfo then
|
|
local classColor = C_ClassColor.GetClassColor(characterInfo.class);
|
|
name = format("%s - %s", classColor:WrapTextInColorCode(characterInfo.name), characterInfo.realm);
|
|
end
|
|
charaterEnumertorList[#charaterEnumertorList+1] = {character,name}
|
|
end
|
|
end
|
|
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
return index, unpack(tbl[index])
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, charaterEnumertorList, 0
|
|
end,
|
|
},
|
|
instanceType = {
|
|
name = L["Instance Type"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
return index, unpack(tbl[index])
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, instanceTypeEnumeratorList, 0
|
|
end,
|
|
},
|
|
disabled = {
|
|
name = L["Enabled"],
|
|
enumerate = function (limitations, includeLimitations, includeOther)
|
|
return function (tbl, index)
|
|
index = index + 1
|
|
if tbl[index] then
|
|
return index, unpack(tbl[index])
|
|
elseif includeOther and index == #tbl + 1 then
|
|
return index, 0, L["Other"]
|
|
end
|
|
end, disabledEnumeratorList, 0
|
|
end,
|
|
},
|
|
}
|