Aleksei-bird 7c9c708c92 Первый фикс
Пачки некоторых позиций увеличены
2024-03-01 20:54:33 +03:00

241 lines
7.5 KiB
Lua

---@class FPModuleSet
---@field modules FPCollection<FPModule>
---@field module_count integer
---@field module_limit integer
---@field empty_slots integer
---@field total_effects ModuleEffects
---@field valid boolean
---@field parent FPMachine | FPBeacon
---@field class "ModuleSet"
-- 'Class' representing a group of modules in a machine or beacon
ModuleSet = {}
function ModuleSet.init(parent)
return {
modules = Collection.init(),
module_count = 0,
module_limit = parent.proto.module_limit,
empty_slots = parent.proto.module_limit,
total_effects = nil, -- summarized by calling function
valid = true,
parent = parent,
class = "ModuleSet"
}
end
function ModuleSet.add(self, proto, amount)
local object = Module.init(proto, amount, self)
local dataset = Collection.add(self.modules, object)
ModuleSet.count_modules(self)
return dataset
end
function ModuleSet.remove(self, dataset)
Collection.remove(self.modules, dataset)
ModuleSet.count_modules(self)
end
function ModuleSet.replace(self, dataset, object)
object.parent = self
local replacement = Collection.replace(self.modules, dataset, object)
ModuleSet.count_modules(self)
return replacement
end
function ModuleSet.clear(self)
self.modules = Collection.init()
ModuleSet.count_modules(self)
end
function ModuleSet.get(self, dataset_id)
return Collection.get(self.modules, dataset_id)
end
function ModuleSet.get_by_name(self, name)
return Collection.get_by_name(self.modules, name)
end
function ModuleSet.get_all(self)
return Collection.get_all(self.modules)
end
function ModuleSet.get_in_order(self, reverse)
return Collection.get_in_order(self.modules, reverse)
end
function ModuleSet.get_module_kind_amount(self)
return self.modules.count
end
function ModuleSet.normalize(self, features)
self.module_limit = self.parent.proto.module_limit
if features.compatibility then ModuleSet.verify_compatibility(self) end
if features.trim then ModuleSet.trim(self) end
if features.sort then ModuleSet.sort(self) end
if features.effects then ModuleSet.summarize_effects(self) end
ModuleSet.count_modules(self)
end
function ModuleSet.count_modules(self)
local count = 0
for _, module in pairs(self.modules.datasets) do
count = count + module.amount
end
self.module_count = count
self.empty_slots = self.module_limit - self.module_count
end
function ModuleSet.verify_compatibility(self)
local modules_to_remove = {}
for _, module in ipairs(ModuleSet.get_in_order(self)) do
if not ModuleSet.check_compatibility(self, module.proto) then
table.insert(modules_to_remove, module)
end
end
-- Actually remove incompatible modules; counts updated by calling function
for _, module in pairs(modules_to_remove) do ModuleSet.remove(self, module) end
end
function ModuleSet.trim(self)
local module_count, module_limit = self.module_count, self.module_limit
-- Return if the module count is within limits
if module_count <= module_limit then return end
local modules_to_remove = {}
-- Traverse modules in reverse to trim them off the end
for _, module in ipairs(ModuleSet.get_in_order(self, true)) do
-- Remove a whole module if it brings the count to >= limit
if (module_count - module.amount) >= module_limit then
table.insert(modules_to_remove, module)
module_count = module_count - module.amount
else -- Otherwise, diminish the amount on the module appropriately and break
local new_amount = module.amount - (module_count - module_limit)
Module.set_amount(module, new_amount)
break
end
end
-- Actually remove superfluous modules; counts updated by calling function
for _, module in pairs(modules_to_remove) do ModuleSet.remove(self, module) end
end
-- Sorts modules in a deterministic fashion so they are in the same order for every line
function ModuleSet.sort(self)
local modules_by_name = {}
for _, module in pairs(ModuleSet.get_all(self)) do
modules_by_name[module.proto.name] = module
end
local next_position = 1
for _, category in ipairs(global.prototypes.modules) do
for _, module_proto in ipairs(category.members) do
local module = modules_by_name[module_proto.name]
if module then
module.gui_position = next_position
next_position = next_position + 1
end
end
end
end
function ModuleSet.summarize_effects(self)
local effects = {consumption = 0, speed = 0, productivity = 0, pollution = 0}
for _, module in pairs(self.modules.datasets) do
for name, effect in pairs(module.total_effects) do
effects[name] = effects[name] + effect
end
end
self.total_effects = effects
_G[self.parent.class].summarize_effects(self.parent)
end
function ModuleSet.check_compatibility(self, module_proto)
return _G[self.parent.class].check_module_compatibility(self.parent, module_proto)
end
function ModuleSet.compile_filter(self)
local compatible_modules = {}
for module_name, module_proto in pairs(MODULE_NAME_MAP) do
if ModuleSet.check_compatibility(self, module_proto) then
table.insert(compatible_modules, module_name)
end
end
local existing_modules = {}
for _, module in pairs(self.modules.datasets) do
table.insert(existing_modules, module.proto.name)
end
return {{filter="name", name=compatible_modules},
{filter="name", mode="and", invert=true, name=existing_modules}}
end
function ModuleSet.paste(self, module)
if not ModuleSet.check_compatibility(self, module.proto) then
return false, "incompatible"
elseif self.empty_slots == 0 then
return false, "no_empty_slots"
end
local desired_amount = math.min(module.amount, self.empty_slots)
local existing_module = ModuleSet.get_by_name(self, module.proto.name)
if existing_module then
Module.set_amount(existing_module, existing_module.amount + desired_amount)
else
ModuleSet.add(self, module.proto, desired_amount)
end
ModuleSet.normalize(self, {sort=true, effects=true})
return true, nil
end
function ModuleSet.pack(self)
return {
modules = Collection.pack(self.modules, Module),
-- count, limit, and empty_slots restored by ensuing validation
class = self.class
}
end
function ModuleSet.unpack(packed_self)
local self = packed_self
self.modules = Collection.unpack(packed_self.modules, self, Module)
return self
end
-- Needs validation: modules
function ModuleSet.validate(self)
self.valid = Collection.validate_datasets(self.modules, Module)
if self.valid and self.parent.valid then
if not self.module_count or not self.empty_slots then -- when validating an unpacked ModuleSet
self.module_limit = self.parent.proto.module_limit
ModuleSet.count_modules(self)
end
-- .normalize doesn't remove incompatible modules here, the above validation already marks them
ModuleSet.normalize(self, {trim=true, sort=true, effects=true})
end
return self.valid
end
-- Needs repair: modules
function ModuleSet.repair(self, _)
Collection.repair_datasets(self.modules, nil, Module)
ModuleSet.normalize(self, {trim=true, sort=true, effects=true})
self.valid = true -- repairing invalid modules removes them, making this set valid
return true
end