215 lines
7.0 KiB
Lua

local locale = require '__rusty-locale__.locale'
local icons = require '__rusty-locale__.icons'
local config = require 'config'
local function hook_newindex(table, hook)
local raw_mt = getmetatable(table) or {}
setmetatable(table, raw_mt)
local super_newindex = raw_mt.__newindex or rawset
function raw_mt.__newindex(self, key, value)
hook(self, key, value, function() return super_newindex(self, key, value); end)
end
end
local function _is_result(item, result, results)
if item == result then return true; end
for _, result in pairs(results or {}) do
if result.name == item then return true; end
end
return false
end
local function is_result(recipe, item)
if _is_result(item, recipe.result, recipe.results) then return true; end
if recipe.normal and _is_result(item, recipe.normal.result, recipe.normal.results) then return true; end
if recipe.expensive and _is_result(item, recipe.expensive.result, recipe.expensive.results) then return true; end
return false
end
local function is_ignored(name)
for _, ignore_name in pairs(config.RECIPES_TO_IGNORE) do
if name:find(ignore_name) then return true; end
end
return false
end
local function is_hidden(recipe) -- Just end me please.
local function is_true(hidden) return hidden == true or hidden == 'true'; end
if recipe.normal then return is_true(recipe.normal.hidden)
else return is_true(recipe.hidden); end
end
local function needs_signal(recipe)
if type(recipe) == 'string' then recipe = data.raw['recipe'][recipe]; end
local name = recipe.name
return not (
is_hidden(recipe)
or is_ignored(name)
or is_result(recipe, name)
or data.raw['virtual-signal'][name]
)
end
local function get_result_name(result) return result.name or result[1]; end
local function get_possible_results(recipe)
local res = {}
local function _get_results(tab)
if tab.result then table.insert(res, tab.result); end
if tab.results then
for _, result in pairs(tab.results) do table.insert(res, get_result_name(result)); end
end
end
_get_results(recipe)
if recipe.expensive then _get_results(recipe.expensive); end
if recipe.normal then _get_results(recipe.normal); end
return res
end
local function get_possible_ingredients(recipe)
local res = {}
local function _get_ingredients(tab)
if tab.ingredients then
for _, ingredient in pairs(tab.ingredients) do table.insert(res, get_result_name(ingredient)); end
end
end
_get_ingredients(recipe)
if recipe.expensive then _get_ingredients(recipe.expensive); end
if recipe.normal then _get_ingredients(recipe.normal); end
return res
end
local function get_max_ingredient_count(recipe)
local max = 0
local function check(tab)
local size = type(tab.ingredients) == 'table' and table_size(tab.ingredients) or 0
if size > max then max = size; end
end
check(recipe)
if recipe.expensive then check(recipe.expensive); end
if recipe.normal then check(recipe.normal); end
return max
end
local function get_order(recipe)
local subgroup_order = (data.raw['item-subgroup'][recipe.subgroup] or {}).order or 'zzz'
local recipe_order = recipe.order or 'zzz'
return subgroup_order..'-'..recipe_order..'['..recipe.name..']'
end
local recipes_waiting_for_groups = {}
local function make_signal_for_recipe(name, recipe)
if needs_signal(recipe) then
if recipe.subgroup and data.raw['item-subgroup'][recipe.subgroup] == nil then
print("Recipe `"..tostring(name).."` needs subgroup `"..tostring(recipe.subgroup).."` which doesn't exist yet - waiting for it to be created...")
recipes_waiting_for_groups[recipe.subgroup] = recipes_waiting_for_groups[recipe.subgroup] or {}
table.insert(recipes_waiting_for_groups[recipe.subgroup], recipe)
return
end
local recipe_icons = icons.of(recipe, true)
if not recipe_icons then
local message = "Recipe `%s` doesn't specify valid icons."
if mods['omnilib'] then message = message.." Please ask the author of said recipe to kindly fix their shit, instead of resorting to lazy cop-outs."; end
log(message:format(name))
hook_newindex(recipe, function(self, key, value, super)
super()
if key == 'icon_size' then make_signal_for_recipe(self.name, self); end
end)
return
end
print("Generating virtual signal for recipe `"..tostring(name).."`")
local subgroup = config.UNSORTED_RECIPE_SUBGROUP
if recipe.subgroup then
local group = data.raw['item-group'][data.raw['item-subgroup'][recipe.subgroup].group]
subgroup = config.RECIPE_SUBGROUP_PREFIX..group.name
if not data.raw['item-subgroup'][subgroup] then
data:extend{{
type = 'item-subgroup',
name = subgroup,
group = config.GROUP_NAME,
order = group.order..'['..group.name..']',
}}
end
end
local locale = locale.of(recipe)
data:extend{{
type = 'virtual-signal',
name = name,
localised_name = {'crafting_combinator.recipe-locale', locale.name},
localised_description = locale.description,
icons = recipe_icons,
subgroup = subgroup,
order = get_order(recipe),
}}
end
end
local rc = data.raw['constant-combinator'][config.RC_PROXY_NAME]
local result_counts = {recipes = {}, uses = {}}
local function process_recipe(name, recipe)
make_signal_for_recipe(name, recipe)
-- Expand the rc slots, just in case there is some insane recipe with a hundred ingredients or something...
local required_slots = get_max_ingredient_count(recipe) + config.RC_SLOT_RESERVE
if required_slots > rc.item_slot_count then
print(("Expanding rc slots to %d"):format(required_slots))
rc.item_slot_count = required_slots
end
--TODO: Do the same for products?
for _, result in pairs(get_possible_results(recipe)) do
local count = (result_counts.recipes[result] or 0) + 1
result_counts.recipes[result] = count
if count + config.RC_SLOT_RESERVE > rc.item_slot_count then
print("Expanding rc slots to fit recipes for "..tostring(result).." ("..tostring(count)..")")
rc.item_slot_count = count + config.RC_SLOT_RESERVE
end
end
for _, ingredient in pairs(get_possible_ingredients(recipe)) do
local count = (result_counts.uses[ingredient] or 0) + 1
result_counts.uses[ingredient] = count
if count + config.RC_SLOT_RESERVE > rc.item_slot_count then
print("Expanding rc slots to fit uses of "..tostring(ingredient).." ("..tostring(count)..")")
rc.item_slot_count = count + config.RC_SLOT_RESERVE
end
end
end
-- Generate signals for all existing recipes that need it
for name, recipe in pairs(data.raw['recipe']) do process_recipe(name, recipe); end
-- Listen for other mods adding recipes beyond this point and make signals for them if necessary
hook_newindex(data.raw['recipe'], function(self, key, value, super)
if value ~= nil then process_recipe(key, value); end --TODO: Remove signals for recipes that get removed
super()
end)
--Listen for other mods adding subgroups, so we can finish processing recipes that need them
hook_newindex(data.raw['item-subgroup'], function(self, key, value, super)
super()
local recipes = recipes_waiting_for_groups[key]
if recipes ~= nil then
recipes_waiting_for_groups[key] = nil
for _, recipe in pairs(recipes) do make_signal_for_recipe(recipe.name, recipe); end
end
end)