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.

406 lines
10 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
-- Items always prioritize above other Types
local acomp = a.itemID;
local bcomp = b.itemID;
if acomp then
if not bcomp then return true; end
elseif bcomp then
return false;
end
-- Otherwise order by container size
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;