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.
398 lines
9.8 KiB
398 lines
9.8 KiB
|
|
-- Sort lib
|
|
local _, app = ...;
|
|
|
|
-- Concepts:
|
|
-- Encapsulates the functionality for performing sort logic against sets of ATT groups
|
|
|
|
-- Global locals
|
|
local ipairs, tostring, type, table_sort, pcall
|
|
= ipairs, tostring, type, table.sort, pcall;
|
|
|
|
-- Module locals
|
|
|
|
-- Sorting Logic
|
|
local sortA, sortB;
|
|
local function calculateSourceQuestDepth(group, text)
|
|
if group.sourceQuestDepth then return group.sourceQuestDepth; end
|
|
if group.sourceQuests then
|
|
local maxDepth, results, depth = 0, nil, nil;
|
|
for i,sourceQuestID in ipairs(group.sourceQuests) do
|
|
results = app.SearchForField("questID", sourceQuestID);
|
|
if results and #results > 0 then
|
|
depth = calculateSourceQuestDepth(results[1]) + 1;
|
|
else
|
|
depth = 1;
|
|
end
|
|
if maxDepth < depth then maxDepth = depth; end
|
|
end
|
|
group.sourceQuestDepth = maxDepth;
|
|
return maxDepth;
|
|
end
|
|
return 0;
|
|
end
|
|
local function toLowerString(value)
|
|
---@diagnostic disable-next-line: undefined-field
|
|
return tostring(value):lower();
|
|
end
|
|
local function calculateAccessibility(source)
|
|
return source.AccessibilityScore or 10000000;
|
|
end
|
|
local function defaultComparison(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
-- If comparing non-tables
|
|
if type(a) ~= "table" or type(b) ~= "table" then
|
|
return a < b;
|
|
end
|
|
local acomp, bcomp;
|
|
-- Maps
|
|
acomp = a.mapID;
|
|
bcomp = b.mapID;
|
|
if acomp then
|
|
if not bcomp then return true; end
|
|
elseif bcomp then
|
|
return false;
|
|
end
|
|
-- Raids/Encounter
|
|
acomp = a.isRaid;
|
|
bcomp = b.isRaid;
|
|
if acomp then
|
|
if not bcomp then return true; end
|
|
elseif bcomp then
|
|
return false;
|
|
end
|
|
-- Headers/Filters/AchievementCategories (or other Types which are used as Headers)
|
|
acomp = a.headerID or a.filterID or a.achievementCategoryID or a.isHeader
|
|
bcomp = b.headerID or b.filterID or b.achievementCategoryID or b.isHeader
|
|
if acomp then
|
|
if not bcomp then return true; end
|
|
elseif bcomp then
|
|
return false;
|
|
end
|
|
-- Quests
|
|
acomp = a.questID;
|
|
bcomp = b.questID;
|
|
if acomp then
|
|
if not bcomp then return true; end
|
|
elseif bcomp then
|
|
return false;
|
|
end
|
|
-- Items
|
|
acomp = a.itemID;
|
|
bcomp = b.itemID;
|
|
if acomp then
|
|
if not bcomp then return true; end
|
|
elseif bcomp then
|
|
return false;
|
|
end
|
|
-- Any two similar-type groups via name
|
|
acomp = toLowerString(a.name);
|
|
bcomp = toLowerString(b.name);
|
|
return acomp < bcomp;
|
|
end
|
|
local function GetGroupSortValue(group)
|
|
-- sub-groups on top
|
|
-- >= 1
|
|
if group.g then
|
|
local total = group.total;
|
|
if total then
|
|
local progress = group.progress;
|
|
-- completed groups at the very top, ordered by their own total
|
|
if total == progress then
|
|
-- 3 <= p
|
|
return 2 + total;
|
|
-- partially completed next
|
|
elseif progress and progress > 0 then
|
|
-- 1 < p <= 2
|
|
return 1 + (progress / total);
|
|
-- no completion, ordered by their own total in reverse
|
|
-- 0 < p <= 1
|
|
else
|
|
return (1 / total);
|
|
end
|
|
end
|
|
-- collectibles next
|
|
-- >= 0
|
|
elseif group.collectible then
|
|
-- = 0.5
|
|
if group.collected then
|
|
return 0.5;
|
|
else
|
|
-- 0 <= p < 0.5
|
|
return (group.sortProgress or 0) / 2;
|
|
end
|
|
-- trackables next
|
|
-- -1 <= p <= -0.5
|
|
elseif group.trackable then
|
|
if group.saved then
|
|
return -0.5;
|
|
else
|
|
return -1;
|
|
end
|
|
-- remaining last
|
|
-- = -2
|
|
else
|
|
return -2;
|
|
end
|
|
end
|
|
local function stringComparison(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
-- Any two similar-type groups with text
|
|
a = toLowerString(a);
|
|
b = toLowerString(b);
|
|
return a < b;
|
|
end
|
|
app.SortDefaults = setmetatable({
|
|
-- Naming Convention: Capital = Special Sort Function, lowercase = field on an object
|
|
Global = defaultComparison,
|
|
Strings = stringComparison,
|
|
Values = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
return a < b;
|
|
end,
|
|
-- Sorts objects first by whether they do not have sub-groups [.g] defined
|
|
Hierarchy = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
local acomp, bcomp;
|
|
acomp = a.g
|
|
bcomp = b.g
|
|
return (acomp and #acomp or 0) < (bcomp and #bcomp or 0);
|
|
end,
|
|
-- Sorts objects first by how many total collectibles they contain
|
|
Total = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
local acomp, bcomp;
|
|
acomp = a.total or 0;
|
|
bcomp = b.total or 0;
|
|
return acomp < bcomp;
|
|
end,
|
|
-- Sorts objects first by their nextEvent.Start
|
|
EventStart = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
local acomp, bcomp;
|
|
acomp = a.nextEvent;
|
|
acomp = acomp and acomp.start or 0;
|
|
bcomp = b.nextEvent;
|
|
bcomp = bcomp and bcomp.start or 0;
|
|
return acomp < bcomp;
|
|
end,
|
|
ClassicQuestOrder = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
if a.isRaid then
|
|
if not b.isRaid then
|
|
return true;
|
|
end
|
|
elseif b.isRaid then
|
|
return false;
|
|
end
|
|
if a.isBreadcrumb then
|
|
if not b.isBreadcrumb then
|
|
return true;
|
|
end
|
|
elseif b.isBreadcrumb then
|
|
return false;
|
|
end
|
|
-- Any two similar-type groups with text
|
|
sortA = toLowerString(a.text);
|
|
sortB = toLowerString(b.text);
|
|
if sortA == sortB and sortA then
|
|
return calculateSourceQuestDepth(a, sortA) < calculateSourceQuestDepth(b, sortB);
|
|
end
|
|
return sortA < sortB;
|
|
end,
|
|
Accessibility = function(a, b)
|
|
return calculateAccessibility(a) < calculateAccessibility(b);
|
|
end,
|
|
name = function(a,b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
-- Any two similar-type groups with text
|
|
a = toLowerString(a.name);
|
|
b = toLowerString(b.name);
|
|
return a < b;
|
|
end,
|
|
text = function(a, b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
-- Any two similar-type groups with text
|
|
a = toLowerString(a.text);
|
|
b = toLowerString(b.text);
|
|
return a < b;
|
|
end,
|
|
textAndLvl = function(a, b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
sortA = a.lvl or 0;
|
|
sortB = b.lvl or 0;
|
|
if sortA < sortB then
|
|
return false;
|
|
elseif sortA == sortB then
|
|
-- Any two similar-type groups with text
|
|
a = toLowerString(a.name or a.text);
|
|
b = toLowerString(b.name or b.text);
|
|
return a < b;
|
|
else
|
|
return true;
|
|
end
|
|
end,
|
|
progress = function(a, b)
|
|
return GetGroupSortValue(a) > GetGroupSortValue(b);
|
|
end,
|
|
IndexOneStrings = function(a,b)
|
|
return stringComparison(a[1], b[1]);
|
|
end,
|
|
}, {
|
|
__index = function(t, sortType)
|
|
if type(sortType) == "function" then
|
|
return sortType;
|
|
end
|
|
local method = function(a, b)
|
|
-- If either object doesn't exist
|
|
if a then
|
|
if not b then
|
|
return true;
|
|
end
|
|
elseif b then
|
|
return false;
|
|
else
|
|
-- neither a or b exists, equality returns false
|
|
return false;
|
|
end
|
|
return a[sortType] < b[sortType];
|
|
end;
|
|
rawset(t, sortType, method);
|
|
return method;
|
|
end,
|
|
});
|
|
local function Sort(t, compare, nested)
|
|
if t then
|
|
table_sort(t, compare);
|
|
if nested then
|
|
for i=#t,1,-1 do
|
|
Sort(t[i].g, compare, nested);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- Safely-sorts a table using a provided comparison function and whether to propogate to nested groups
|
|
-- Wrapping in a pcall since sometimes the sorted values are able to change while being within the sort method. This causes the 'invalid sort order function' error
|
|
app.Sort = function(t, compare, nested)
|
|
return pcall(Sort, t, compare or defaultComparison, nested);
|
|
end
|
|
-- Sorts a group using the provided sortType, whether to recurse through nested groups, and whether sorting should only take place given the group having a conditional field
|
|
local function SortGroup(group, sortType)
|
|
-- app.PrintDebug("SortGroup", group.parent and group.parent.text, group.text, sortType);
|
|
if group.g then
|
|
-- either sort visible groups or by conditional
|
|
if group.visible then
|
|
-- app.PrintDebug("sorting",group.hash,"by",sortType)
|
|
local status,err = app.Sort(group.g, app.SortDefaults[sortType]);
|
|
if status then
|
|
-- Setting this to false instead of nil causes the field to also
|
|
-- ignore inherited settings, such as from its base class.
|
|
if group.SortType and not group.PersistSortType then group.SortType = false; end
|
|
else
|
|
-- Uncomment this to debug errors in your sort functions
|
|
-- app.PrintDebug("Error in sort", err);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
app.SortGroup = SortGroup;
|
|
|