339 lines
10 KiB
Lua
339 lines
10 KiB
Lua
local config = require 'config'
|
|
local util = require 'script.util'
|
|
local gui = require 'script.gui'
|
|
local settings_parser = require 'script.settings-parser'
|
|
local recipe_selector = require 'script.recipe-selector'
|
|
local signals = require 'script.signals'
|
|
|
|
|
|
local _M = {}
|
|
local combinator_mt = {__index = _M}
|
|
|
|
|
|
_M.settings_parser = settings_parser {
|
|
mode = {'m', 'string'},
|
|
multiply_by_input = {'i', 'bool'},
|
|
divide_by_output = {'o', 'bool'},
|
|
differ_output = {'d', 'bool'},
|
|
time_multiplier = {'t', 'number'},
|
|
}
|
|
|
|
|
|
-- General housekeeping
|
|
|
|
function _M.init_global()
|
|
global.rc = global.rc or {}
|
|
global.rc.data = global.rc.data or {}
|
|
global.rc.ordered = global.rc.ordered or {}
|
|
end
|
|
|
|
function _M.build_machine_cache()
|
|
_M.item_map = {}
|
|
_M.category_map = {}
|
|
for name, prototype in pairs(game.entity_prototypes) do
|
|
if prototype.crafting_categories and prototype.items_to_place_this then
|
|
for category in pairs(prototype.crafting_categories) do
|
|
_M.category_map[category] = _M.category_map[category] or {}
|
|
for _, item in pairs(prototype.items_to_place_this) do
|
|
_M.item_map[item.name] = {}
|
|
table.insert(_M.category_map[category], item.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for _, recipe in pairs(game.recipe_prototypes) do
|
|
for _, product in pairs(recipe.products) do
|
|
if _M.item_map[product.name] ~= nil then
|
|
table.insert(_M.item_map[product.name], recipe.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local _rc_slot_count = nil
|
|
function _M.get_rc_slot_count()
|
|
if _rc_slot_count == nil then _rc_slot_count = game.entity_prototypes[config.RC_PROXY_NAME].item_slot_count; end
|
|
return _rc_slot_count
|
|
end
|
|
|
|
function _M.on_load()
|
|
for _, combinator in pairs(global.rc.data) do setmetatable(combinator, combinator_mt); end
|
|
end
|
|
|
|
|
|
-- Lifecycle events
|
|
|
|
function _M.create(entity)
|
|
local combinator = setmetatable({
|
|
entity = entity,
|
|
output_proxy = entity.surface.create_entity {
|
|
name = config.RC_PROXY_NAME,
|
|
position = entity.position,
|
|
force = entity.force,
|
|
create_build_effect_smoke = false,
|
|
},
|
|
input_control_behavior = entity.get_or_create_control_behavior(),
|
|
settings = _M.settings_parser:read_or_default(entity, util.deepcopy(config.RC_DEFAULT_SETTINGS)),
|
|
last_signal = false,
|
|
last_name = false,
|
|
last_count = false,
|
|
}, combinator_mt)
|
|
|
|
entity.connect_neighbour {
|
|
wire = defines.wire_type.red,
|
|
target_entity = combinator.output_proxy,
|
|
source_circuit_id = defines.circuit_connector_id.combinator_output,
|
|
}
|
|
entity.connect_neighbour {
|
|
wire = defines.wire_type.green,
|
|
target_entity = combinator.output_proxy,
|
|
source_circuit_id = defines.circuit_connector_id.combinator_output,
|
|
}
|
|
combinator.output_proxy.destructible = false
|
|
combinator.control_behavior = combinator.output_proxy.get_or_create_control_behavior()
|
|
|
|
global.rc.data[entity.unit_number] = combinator
|
|
table.insert(global.rc.ordered, combinator)
|
|
end
|
|
|
|
function _M.destroy(entity)
|
|
local unit_number = entity.unit_number
|
|
local combinator = global.rc.data[unit_number]
|
|
|
|
combinator.output_proxy.destroy()
|
|
settings_parser.destroy(entity)
|
|
signals.cache.drop(entity)
|
|
|
|
global.rc.data[unit_number] = nil
|
|
for k, v in pairs(global.rc.ordered) do
|
|
if v.entity.unit_number == unit_number then
|
|
table.remove(global.rc.ordered, k)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function _M:update(forced)
|
|
if forced then
|
|
self.last_signal = false
|
|
self.last_name = false
|
|
self.last_count = false
|
|
end
|
|
|
|
if self.settings.mode == 'rec' or self.settings.mode == 'use' then self:find_recipe()
|
|
elseif self.settings.mode == 'mac' then self:find_machines(forced)
|
|
else self:find_ingredients_and_products(forced); end
|
|
end
|
|
|
|
|
|
local DUMMY_SIGNAL = {type = 'virtual', name = config.TIME_SIGNAL_NAME}
|
|
local param_cache = {}
|
|
local function make_params(size)
|
|
local params = param_cache[size]
|
|
if not params then
|
|
params = {}
|
|
for i = 1, size do params[i] = {index = i}; end
|
|
param_cache[size] = params
|
|
end
|
|
return params
|
|
end
|
|
|
|
function _M:find_recipe()
|
|
local changed, recipes, count, signal = recipe_selector.get_recipes(
|
|
self.entity, defines.circuit_connector_id.combinator_input,
|
|
self.settings.mode == 'rec' and 'products' or 'ingredients',
|
|
self.last_signal, self.settings.multiply_by_input and self.last_count or nil
|
|
)
|
|
|
|
if not changed then return; end
|
|
self.last_signal = signal
|
|
self.last_count = count
|
|
|
|
local params = make_params(table_size(recipes))
|
|
local index = 1
|
|
local slots = _M.get_rc_slot_count()
|
|
|
|
count = self.settings.multiply_by_input and count or 1
|
|
local round = self.settings.mode == 'use' and math.floor or math.ceil
|
|
for i, recipe in pairs(recipes) do
|
|
local param = params[i]
|
|
if not recipe.recipe.hidden and recipe.recipe.enabled then
|
|
param.signal = recipe_selector.get_signal(recipe.recipe.name)
|
|
param.count = self.settings.differ_output and index or (self.settings.divide_by_output and round(count/recipe.amount) or count)
|
|
param.index = index
|
|
index = index + 1
|
|
elseif slots > index then
|
|
param.signal = DUMMY_SIGNAL
|
|
param.count = 0
|
|
param.index = slots
|
|
slots = slots - 1
|
|
end
|
|
end
|
|
|
|
self.control_behavior.parameters = params
|
|
end
|
|
|
|
function _M:find_ingredients_and_products()
|
|
local changed, recipe, input_count = recipe_selector.get_recipe(
|
|
self.entity,
|
|
defines.circuit_connector_id.combinator_input,
|
|
self.last_name,
|
|
self.settings.multiply_by_input and self.last_count or nil
|
|
)
|
|
|
|
if not changed then return; end
|
|
|
|
self.last_name = recipe and recipe.name
|
|
self.last_count = input_count
|
|
|
|
if recipe and (recipe.hidden or not recipe.enabled) then recipe = nil; end
|
|
|
|
local params = {}
|
|
|
|
if recipe then
|
|
local crafting_multiplier = self.settings.multiply_by_input and input_count or 1
|
|
for i, ing in pairs(
|
|
self.settings.mode == 'prod' and recipe.products or
|
|
self.settings.mode == 'ing' and recipe.ingredients or {}
|
|
) do
|
|
local amount = math.ceil(
|
|
tonumber(ing.amount or ing.amount_min or ing.amount_max) * crafting_multiplier
|
|
* (tonumber(ing.probability) or 1)
|
|
)
|
|
|
|
params[i] = {
|
|
signal = {type = ing.type, name = ing.name},
|
|
count = self.settings.differ_output and i or util.simulate_overflow(amount),
|
|
index = i,
|
|
}
|
|
end
|
|
|
|
table.insert(params, {
|
|
signal = {type = 'virtual', name = config.TIME_SIGNAL_NAME},
|
|
count = util.simulate_overflow(math.floor(tonumber(recipe.energy) * self.settings.time_multiplier * crafting_multiplier)),
|
|
index = _M.get_rc_slot_count(),
|
|
})
|
|
end
|
|
|
|
self.control_behavior.parameters = params
|
|
end
|
|
|
|
|
|
function _M:find_machines()
|
|
local changed, recipe, input_count = recipe_selector.get_recipe(
|
|
self.entity,
|
|
defines.circuit_connector_id.combinator_input,
|
|
self.last_name,
|
|
self.settings.multiply_by_input and self.last_count or nil
|
|
)
|
|
|
|
if not changed then return; end
|
|
|
|
self.last_name = recipe and recipe.name
|
|
self.last_count = input_count
|
|
|
|
if recipe and (recipe.hidden or not recipe.enabled) then recipe = nil; end
|
|
|
|
if _M.item_map == nil then _M.build_machine_cache(); end
|
|
|
|
local params = {}
|
|
local index = 1
|
|
if recipe and recipe.category then
|
|
for _, item in pairs(_M.category_map[recipe.category] or {}) do
|
|
for _, recipe in pairs(_M.item_map[item]) do
|
|
local mac_res = self.entity.force.recipes[recipe]
|
|
if mac_res and not mac_res.hidden and mac_res.enabled then
|
|
table.insert(params, {
|
|
signal = recipe_selector.get_signal(item),
|
|
count = self.settings.multiply_by_input and input_count or
|
|
self.settings.differ_output and index or 1,
|
|
index = index,
|
|
})
|
|
index = index + 1
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.control_behavior.parameters = params
|
|
end
|
|
|
|
|
|
function _M:open(player_index)
|
|
local root = gui.entity(self.entity, {
|
|
gui.section {
|
|
name = 'mode',
|
|
gui.radio('ing', self.settings.mode, {locale='mode-ing', tooltip=true}),
|
|
gui.radio('prod', self.settings.mode, {locale='mode-prod', tooltip=true}),
|
|
gui.radio('use', self.settings.mode, {locale='mode-use', tooltip=true}),
|
|
gui.radio('rec', self.settings.mode, {locale='mode-rec', tooltip=true}),
|
|
gui.radio('mac', self.settings.mode, {locale='mode-mac', tooltip=true}),
|
|
},
|
|
gui.section {
|
|
name = 'misc',
|
|
gui.checkbox('multiply-by-input', self.settings.multiply_by_input, {tooltip=true}),
|
|
gui.checkbox('divide-by-output', self.settings.divide_by_output, {tooltip=true}),
|
|
gui.checkbox('differ-output', self.settings.differ_output, {tooltip=true}),
|
|
gui.number_picker('time-multiplier', self.settings.time_multiplier),
|
|
}
|
|
}):open(player_index)
|
|
|
|
self:update_disabled_checkboxes(root)
|
|
end
|
|
|
|
function _M:on_checked_changed(name, state, element)
|
|
local category, name = name:gsub(':.*$', ''), name:gsub('^.-:', ''):gsub('-', '_')
|
|
if category == 'mode' then
|
|
self.settings.mode = name
|
|
for _, el in pairs(element.parent.children) do
|
|
if el.type == 'radiobutton' then
|
|
local _, _, el_name = gui.parse_entity_gui_name(el.name)
|
|
el.state = el_name == 'mode:'..name
|
|
end
|
|
end
|
|
end
|
|
if category == 'misc' then self.settings[name] = state; end
|
|
|
|
self:update_disabled_checkboxes(gui.get_root(element))
|
|
|
|
self.settings_parser:update(self.entity, self.settings)
|
|
self:update(true)
|
|
end
|
|
|
|
function _M:update_disabled_checkboxes(root)
|
|
self:disable_checkbox(root, 'misc:divide-by-output', 'divide_by_output',
|
|
(self.settings.mode == 'rec' or self.settings.mode == 'use') and not self.settings.differ_output)
|
|
self:disable_checkbox(root, 'misc:multiply-by-input', 'multiply_by_input',
|
|
not self.settings.divide_by_output and not self.settings.differ_output,
|
|
self.settings.divide_by_output or self.settings.multiply_by_input)
|
|
self:disable_checkbox(root, 'misc:differ-output', 'differ_output',
|
|
not self.settings.multiply_by_input)
|
|
end
|
|
|
|
function _M:disable_checkbox(root, name, setting_name, enable, set_state)
|
|
set_state = set_state or false
|
|
local checkbox = gui.find_element(root, gui.name(self.entity, name))
|
|
if checkbox.enabled ~= enable then
|
|
checkbox.enabled = enable
|
|
checkbox.state = set_state
|
|
self.settings[setting_name] = set_state
|
|
end
|
|
end
|
|
|
|
function _M:on_text_changed(name, text)
|
|
if name == 'misc:time-multiplier:value' then
|
|
self.settings.time_multiplier = tonumber(text) or self.settings.time_multiplier
|
|
self.settings_parser:update(self.entity, self.settings)
|
|
self:update(true)
|
|
end
|
|
end
|
|
|
|
|
|
function _M:update_inner_positions()
|
|
settings_parser.move_entity(self.entity, self.output_proxy.position)
|
|
self.output_proxy.teleport(self.entity.position)
|
|
end
|
|
|
|
|
|
return _M
|