---@class FPMachine ---@field proto FPMachinePrototype ---@field count number ---@field limit number? ---@field force_limit boolean ---@field fuel FPFuel? ---@field module_set FPModuleSet ---@field total_effects ModuleEffects ---@field effects_tooltip string ---@field valid boolean ---@field parent FPLine ---@field class "Machine" -- Class representing a machine with its attached modules and fuel Machine = {} function Machine.init(proto, parent) local machine = { proto = proto, count = 0, limit = nil, -- will be set by the user force_limit = true, -- ignored if limit is not set fuel = nil, -- needs to be set by calling Machine.find_fuel afterwards module_set = nil, -- set right below total_effects = nil, effects_tooltip = "", valid = true, parent = parent, class = "Machine" } machine.module_set = ModuleSet.init(machine) return machine end function Machine.normalize_fuel(self, player) if self.proto.energy_type ~= "burner" then self.fuel = nil; return end -- no need to continue if this machine doesn't have a burner local burner = self.proto.burner -- Check if fuel has a valid category for this machine, replace otherwise if self.fuel and not burner.categories[self.fuel.proto.category] then self.fuel = nil end -- If this machine has fuel already, don't replace it if self.fuel == nil then -- Use the first category of this machine's burner as the default one local fuel_category_name, _ = next(burner.categories, nil) local fuel_category_id = PROTOTYPE_MAPS.fuels[fuel_category_name].id local default_fuel_proto = prototyper.defaults.get(player, "fuels", fuel_category_id) self.fuel = Fuel.init(default_fuel_proto) self.fuel.parent = self end end function Machine.summarize_effects(self, mining_prod) local effects = self.module_set.total_effects effects["base_prod"] = self.proto.base_productivity or nil effects["mining_prod"] = mining_prod or nil self.total_effects = effects self.effects_tooltip = util.gui.format_module_effects(effects, false) Line.summarize_effects(self.parent) end function Machine.check_module_compatibility(self, module_proto) local recipe = self.parent.recipe if self.proto.module_limit == 0 then return false end if next(module_proto.limitations) and recipe.proto.use_limitations and not module_proto.limitations[recipe.proto.name] then return false end local allowed_effects = self.proto.allowed_effects if allowed_effects == nil then return false else for effect_name, _ in pairs(module_proto.effects) do if allowed_effects[effect_name] == false then return false end end end return true end function Machine.paste(self, object) if object.class == "Machine" then -- See if the pasted machine also exists in this machine's category, and use it if compatible local found_machine = prototyper.util.find_prototype("machines", object.proto.name, self.proto.category) if found_machine and Line.is_machine_applicable(self.parent, found_machine) then self.parent.machine = object object.parent = self.parent object.proto = found_machine -- make sure to set this to the right category machine ModuleSet.normalize(object.module_set, {compatibility=true, effects=true}) Line.summarize_effects(object.parent) return true, nil else return false, "incompatible" end elseif object.class == "Module" then return ModuleSet.paste(self.module_set, object) else return false, "incompatible_class" end end function Machine.clone(self) local clone = Machine.unpack(Machine.pack(self)) clone.parent = self.parent clone.count = self.count -- keep around to avoid recalc being needed Machine.validate(clone) return clone end function Machine.pack(self) return { proto = prototyper.util.simplify_prototype(self.proto, self.proto.category), limit = self.limit, force_limit = self.force_limit, fuel = (self.fuel) and Fuel.pack(self.fuel) or nil, module_set = ModuleSet.pack(self.module_set), class = self.class } end function Machine.unpack(packed_self) local self = packed_self self.fuel = (packed_self.fuel) and Fuel.unpack(packed_self.fuel) or nil if self.fuel then self.fuel.parent = self end self.module_set = ModuleSet.unpack(packed_self.module_set) self.module_set.parent = self return self end -- Needs validation: proto, fuel, module_set function Machine.validate(self) self.proto = prototyper.util.validate_prototype_object(self.proto, "category") self.valid = (not self.proto.simplified) local parent_line = self.parent if self.valid and parent_line.valid and parent_line.recipe.valid then self.valid = Line.is_machine_applicable(parent_line, self.proto) end -- If the machine changed to not use a burner, remove its fuel if not self.proto.burner then self.fuel = nil end if self.fuel then self.valid = Fuel.validate(self.fuel) and self.valid end self.valid = ModuleSet.validate(self.module_set) and self.valid return self.valid end -- Needs repair: proto, fuel, module_set function Machine.repair(self, player) -- If the prototype is still simplified, it couldn't be fixed by validate -- A final possible fix is to replace this machine with the default for its category if self.proto.simplified and not Line.change_machine_to_default(self.parent, player) then return false -- if this happens, the whole line can not be salvaged end self.valid = true -- if it gets to this, change_machine was successful and the machine is valid -- It just might need to cleanup some fuel and/or modules if self.fuel and not self.fuel.valid and not Fuel.repair(self.fuel) then -- If fuel is unrepairable, replace it with a default value self.fuel = nil Machine.normalize_fuel(self, player) end -- Remove invalid modules and normalize the remaining ones ModuleSet.repair(self.module_set) return true end