210 lines
6.3 KiB
Lua
210 lines
6.3 KiB
Lua
---@class FPCollection<T>
|
|
---@field datasets { [integer]: `T` }
|
|
---@field index integer
|
|
---@field count integer
|
|
---@field class "Collection"
|
|
|
|
-- 'Class' representing a list of objects/datasets with some useful methods
|
|
-- (An object only becomes a dataset once it is added to the collection)
|
|
Collection = {}
|
|
|
|
function Collection.init()
|
|
return {
|
|
datasets = {},
|
|
index = 0,
|
|
count = 0,
|
|
class = "Collection"
|
|
}
|
|
end
|
|
|
|
|
|
-- Adds given object to the end of the collection
|
|
function Collection.add(self, object)
|
|
if not object then error("Can't insert nil dataset") end
|
|
|
|
self.index = self.index + 1
|
|
object.id = self.index
|
|
self.datasets[self.index] = object
|
|
|
|
self.count = self.count + 1
|
|
object.gui_position = self.count
|
|
|
|
return object -- Returning it here feels nice
|
|
end
|
|
|
|
-- Inserts the given object at the given position, shifting other elements down
|
|
function Collection.insert_at(self, gui_position, object)
|
|
if not object then error("Can't insert nil dataset") end
|
|
if not gui_position then error("Can't insert at nil position") end
|
|
|
|
self.index = self.index + 1
|
|
object.id = self.index
|
|
|
|
self.count = self.count + 1
|
|
object.gui_position = gui_position
|
|
|
|
for _, dataset in pairs(self.datasets) do
|
|
if dataset.gui_position >= gui_position then
|
|
dataset.gui_position = dataset.gui_position + 1
|
|
end
|
|
end
|
|
|
|
self.datasets[self.index] = object
|
|
return object
|
|
end
|
|
|
|
function Collection.remove(self, dataset)
|
|
if not dataset then error("Can't remove nil dataset") end
|
|
|
|
-- Move positions of datasets after the deleted one down by one
|
|
for _, d in pairs(self.datasets) do
|
|
if d.gui_position > dataset.gui_position then
|
|
d.gui_position = d.gui_position - 1
|
|
end
|
|
end
|
|
|
|
self.count = self.count - 1
|
|
self.datasets[dataset.id] = nil
|
|
|
|
if self.count ~= table_size(self.datasets) then error("Dataset count incorrect") end
|
|
|
|
-- Returning the deleted position here to allow for GUI adjustments
|
|
return dataset.gui_position
|
|
end
|
|
|
|
-- Replaces the dataset with the new object in-place
|
|
function Collection.replace(self, dataset, object)
|
|
if not dataset then error("Can't replace nil dataset") end
|
|
if not object then error("Can't replace with nil object") end
|
|
|
|
object.id = dataset.id
|
|
object.gui_position = dataset.gui_position
|
|
self.datasets[dataset.id] = object
|
|
return object -- Returning it here feels nice
|
|
end
|
|
|
|
|
|
function Collection.get(self, object_id)
|
|
return self.datasets[object_id]
|
|
end
|
|
|
|
-- For when order doesn't matter
|
|
function Collection.get_all(self)
|
|
return self.datasets
|
|
end
|
|
|
|
-- Return format: {[gui_position] = dataset}
|
|
function Collection.get_in_order(self, reverse)
|
|
local ordered_datasets = {}
|
|
for _, dataset in pairs(self.datasets) do
|
|
local table_position = (reverse) and (self.count - dataset.gui_position + 1) or dataset.gui_position
|
|
ordered_datasets[table_position] = dataset
|
|
end
|
|
return ordered_datasets
|
|
end
|
|
|
|
-- Returns the dataset specified by the gui_position
|
|
function Collection.get_by_gui_position(self, gui_position)
|
|
if gui_position == 0 then return nil end
|
|
for _, dataset in pairs(self.datasets) do
|
|
if dataset.gui_position == gui_position then
|
|
return dataset
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Returns the dataset with the given name, nil if it doesn't exist
|
|
function Collection.get_by_name(self, name)
|
|
for _, dataset in pairs(self.datasets) do
|
|
-- Check against the prototype, if it exists
|
|
local check_against = dataset.proto or dataset
|
|
if check_against.name == name then
|
|
return dataset
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Returns the dataset with the given type and name, nil if it doesn't exist
|
|
function Collection.get_by_type_and_name(self, type_name, name)
|
|
for _, dataset in pairs(self.datasets) do
|
|
-- Check against the prototype, if it exists
|
|
local check_against = dataset.proto or dataset
|
|
if check_against.type == type_name and check_against.name == name then
|
|
return dataset
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function Collection.shift(self, main_dataset, first_position, direction, spots)
|
|
if not main_dataset then error("Can't shift nil dataset") end
|
|
if not(direction == "negative" or direction == "positive") then error("Can't shift in invalid direction") end
|
|
|
|
local original_position = main_dataset.gui_position
|
|
local new_position = nil
|
|
|
|
if spots == nil then -- means shift-to-end
|
|
new_position = (direction == "positive") and self.count or first_position
|
|
else
|
|
if direction == "positive" then
|
|
new_position = math.min(original_position + spots, self.count)
|
|
else
|
|
new_position = math.max(original_position - spots, first_position)
|
|
end
|
|
end
|
|
|
|
Collection.remove(self, main_dataset)
|
|
Collection.insert_at(self, new_position, main_dataset)
|
|
end
|
|
|
|
|
|
-- Packs every dataset in this collection
|
|
function Collection.pack(self, object_class)
|
|
local packed_collection = {
|
|
objects = {},
|
|
class = self.class
|
|
}
|
|
|
|
for _, dataset in ipairs(Collection.get_in_order(self)) do
|
|
table.insert(packed_collection.objects, object_class.pack(dataset))
|
|
end
|
|
|
|
return packed_collection
|
|
end
|
|
|
|
-- Unpacks every dataset in this collection
|
|
function Collection.unpack(packed_self, parent, object_class)
|
|
local self = Collection.init()
|
|
self.class = packed_self.class
|
|
|
|
for _, object in ipairs(packed_self.objects) do -- packed objects already in array order
|
|
local dataset = Collection.add(self, object_class.unpack(object))
|
|
dataset.parent = parent
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
-- Updates the validity of all datasets in this collection
|
|
function Collection.validate_datasets(self, object_class)
|
|
local valid = true
|
|
|
|
for _, dataset in pairs(self.datasets) do
|
|
-- Stays true until a single dataset is invalid, then stays false
|
|
valid = object_class.validate(dataset) and valid
|
|
end
|
|
|
|
return valid
|
|
end
|
|
|
|
-- Removes any invalid, unrepairable datasets from the collection
|
|
function Collection.repair_datasets(self, player, object_class)
|
|
for _, dataset in pairs(self.datasets) do
|
|
if not dataset.valid and not object_class.repair(dataset, player) then
|
|
_G[dataset.parent.class].remove(dataset.parent, dataset)
|
|
end
|
|
end
|
|
end
|