so (i have no idea about lua and/or modding) i have found that a recipe is simply taking an ingredient (or multiple) and creating an output (or multiple) and it uses "AND" in that manner of speaking;
so this recipe simply uses 1 basic-inserter AND 2 electronic-circuit AND 10 iron-plates. i am trying to create an assembly machine like player (uses lower level ingredients if it is necessary) so for the sake of argument i am looking for a way to convert this recipe to;
is it possible? or is there way to create an assembly machine like this?
Re: using "OR" in recipes not "AND"
Posted: Thu Feb 12, 2015 7:25 pm
by hoho
The recipes list is essentially an array of ingredients. It's not really possible to add logic operations between them without modifying the base game on C++ level.
What if there are two different recipes for making iron plates, how would you choose between them?
It might be possible with a huge hack of traversing the recipe table and manually figuring out what can be made from what inside the "assembler" and I'm not even sure if that would be possible.
Re: using "OR" in recipes not "AND"
Posted: Fri Feb 13, 2015 2:40 am
by FreeER
It's not currently possible to do so in that manner, but you can actually have separate recipes that produce the same thing with different ingredients. Since the prototypes are written in a Lua file you can actually write a function to create those separate recipes, perhaps something like so (note I've barely tested this and it's not optimized at all sooo, keep that in mind):
-- takes a recipe to make duplicates of
-- and an 'equality table' which describes
-- which ingredients can be replaced with
-- which other ingredients
function makeDuplicates(recipe, equalityTable)
-- store made recipes
local recipes = {}
-- treat given recipe as the first 'made' recipe
recipes[1] = recipe
-- loop over each replaceable ingredient
for ingredient, equalTable in pairs(equalityTable) do
-- loop over each replacement for the replaceable ingredient
for _, replacementIngredient in ipairs(equalTable) do
-- (potentially) make a copy of each recipe
-- use 'regular' (not so regular in Lua lol) for loop
-- since we are inserting recipes and would have an
-- infinite loop using ipairs
for i=1, #recipes do
local made_recipe = recipes[i]
-- find the index of the replaceable ingredient in the recipe
local index = findIngredientIndex(made_recipe, ingredient)
-- if it's in the recipe
if index then
-- copy the recipe
local newRecipe = table.deepcopy(made_recipe)
-- change the name
newRecipe.name = newRecipe.name.."_"..tostring(#recipes)
-- replace the ingredient
newRecipe.ingredients[index] = replacementIngredient
-- add it to the recipes table
table.insert(recipes, newRecipe)
end
end
end
end
-- return the recipes, could just as easily extend data.raw here instead
return recipes
end
-- takes a recipe to search and an ingredient to find
-- returns the index in the ingredients table of the
-- ingredient or nil
function findIngredientIndex(recipe, ingredient)
for index, recipeIngredient in ipairs(recipe.ingredients) do
if table.compare(recipeIngredient, ingredient) then
return index
end
end
return nil
end
-- from util
function table.deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
-- don't copy factorio rich objects
elseif object.__self then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
-- also from util
function table.compare( tbl1, tbl2 )
for k, v in pairs( tbl1 ) do
if type(v) == "table" and type(tbl2[k]) == "table" then
if not table.compare( v, tbl2[k] ) then return false end
else
if ( v ~= tbl2[k] ) then return false end
end
end
for k, v in pairs( tbl2 ) do
if type(v) == "table" and type(tbl1[k]) == "table" then
if not table.compare( v, tbl1[k] ) then return false end
else
if v ~= tbl1[k] then return false end
end
end
return true
end
-- takes a recipe to make duplicates of
-- and an 'equality table' which describes
-- which ingredients can be replaced with
-- which other ingredients
function makeDuplicates(recipe, equalityTable)
-- store made recipes
local recipes = {}
-- treat given recipe as the first 'made' recipe
recipes[1] = recipe
-- loop over each replaceable ingredient
for ingredient, equalTable in pairs(equalityTable) do
-- loop over each replacement for the replaceable ingredient
for _, replacementIngredient in ipairs(equalTable) do
-- (potentially) make a copy of each recipe
-- use 'regular' (not so regular in Lua lol) for loop
-- since we are inserting recipes and would have an
-- infinite loop using ipairs
for i=1, #recipes do
local made_recipe = recipes[i]
-- find the index of the replaceable ingredient in the recipe
local index = findIngredientIndex(made_recipe, ingredient)
-- if it's in the recipe
if index then
-- copy the recipe
local newRecipe = table.deepcopy(made_recipe)
-- change the name
newRecipe.name = newRecipe.name.."_"..tostring(#recipes)
-- replace the ingredient
newRecipe.ingredients[index] = replacementIngredient
-- add it to the recipes table
table.insert(recipes, newRecipe)
end
end
end
end
-- return the recipes, could just as easily extend data.raw here instead
return recipes
end
-- takes a recipe to search and an ingredient to find
-- returns the index in the ingredients table of the
-- ingredient or nil
function findIngredientIndex(recipe, ingredient)
for index, recipeIngredient in ipairs(recipe.ingredients) do
if table.compare(recipeIngredient, ingredient) then
return index
end
end
return nil
end
local recipe = {
type = "recipe",
name = "express-splitter",
category = "crafting-with-fluid",
enabled = "false",
energy_required = 2,
ingredients =
{
{"fast-splitter", 1},
{"iron-gear-wheel", 10},
{"advanced-circuit", 10},
{type="fluid", name="lubricant", amount=8}
},
result = "express-splitter"
}
local made = makeDuplicates(recipe, {
-- table of ingredient is key
-- it's value is a table of replacement ingredients
[{"iron-gear-wheel", 10}] = {
{"iron-ore", 20},
{"copper-ore", 50}
},
[{type="fluid", name="lubricant", amount=8}] = {
{"copper-plate", 3},
{type="fluid", name="water", amount=80}
}
})
for _, recipe in ipairs(made) do
print("\n\n"..recipe.name)
for _, ingredient in pairs(recipe.ingredients) do
local name = (ingredient[1] or ingredient.name)
local count = (ingredient[2] or ingredient.amount)
print("{"..name..", "..count.."}")
end
end
of course with lots of possible replacements it'd flood the recipe list a bit...
Re: using "OR" in recipes not "AND"
Posted: Sat Feb 14, 2015 6:21 am
by ares0027
FreeER wrote:It's not currently possible to do so in that manner, but you can actually have separate recipes that produce the same thing with different ingredients. Since the prototypes are written in a Lua file you can actually write a function to create those separate recipes, perhaps something like so (note I've barely tested this and it's not optimized at all sooo, keep that in mind):
-- takes a recipe to make duplicates of
-- and an 'equality table' which describes
-- which ingredients can be replaced with
-- which other ingredients
function makeDuplicates(recipe, equalityTable)
-- store made recipes
local recipes = {}
-- treat given recipe as the first 'made' recipe
recipes[1] = recipe
-- loop over each replaceable ingredient
for ingredient, equalTable in pairs(equalityTable) do
-- loop over each replacement for the replaceable ingredient
for _, replacementIngredient in ipairs(equalTable) do
-- (potentially) make a copy of each recipe
-- use 'regular' (not so regular in Lua lol) for loop
-- since we are inserting recipes and would have an
-- infinite loop using ipairs
for i=1, #recipes do
local made_recipe = recipes[i]
-- find the index of the replaceable ingredient in the recipe
local index = findIngredientIndex(made_recipe, ingredient)
-- if it's in the recipe
if index then
-- copy the recipe
local newRecipe = table.deepcopy(made_recipe)
-- change the name
newRecipe.name = newRecipe.name.."_"..tostring(#recipes)
-- replace the ingredient
newRecipe.ingredients[index] = replacementIngredient
-- add it to the recipes table
table.insert(recipes, newRecipe)
end
end
end
end
-- return the recipes, could just as easily extend data.raw here instead
return recipes
end
-- takes a recipe to search and an ingredient to find
-- returns the index in the ingredients table of the
-- ingredient or nil
function findIngredientIndex(recipe, ingredient)
for index, recipeIngredient in ipairs(recipe.ingredients) do
if table.compare(recipeIngredient, ingredient) then
return index
end
end
return nil
end
-- from util
function table.deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
-- don't copy factorio rich objects
elseif object.__self then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
-- also from util
function table.compare( tbl1, tbl2 )
for k, v in pairs( tbl1 ) do
if type(v) == "table" and type(tbl2[k]) == "table" then
if not table.compare( v, tbl2[k] ) then return false end
else
if ( v ~= tbl2[k] ) then return false end
end
end
for k, v in pairs( tbl2 ) do
if type(v) == "table" and type(tbl1[k]) == "table" then
if not table.compare( v, tbl1[k] ) then return false end
else
if v ~= tbl1[k] then return false end
end
end
return true
end
-- takes a recipe to make duplicates of
-- and an 'equality table' which describes
-- which ingredients can be replaced with
-- which other ingredients
function makeDuplicates(recipe, equalityTable)
-- store made recipes
local recipes = {}
-- treat given recipe as the first 'made' recipe
recipes[1] = recipe
-- loop over each replaceable ingredient
for ingredient, equalTable in pairs(equalityTable) do
-- loop over each replacement for the replaceable ingredient
for _, replacementIngredient in ipairs(equalTable) do
-- (potentially) make a copy of each recipe
-- use 'regular' (not so regular in Lua lol) for loop
-- since we are inserting recipes and would have an
-- infinite loop using ipairs
for i=1, #recipes do
local made_recipe = recipes[i]
-- find the index of the replaceable ingredient in the recipe
local index = findIngredientIndex(made_recipe, ingredient)
-- if it's in the recipe
if index then
-- copy the recipe
local newRecipe = table.deepcopy(made_recipe)
-- change the name
newRecipe.name = newRecipe.name.."_"..tostring(#recipes)
-- replace the ingredient
newRecipe.ingredients[index] = replacementIngredient
-- add it to the recipes table
table.insert(recipes, newRecipe)
end
end
end
end
-- return the recipes, could just as easily extend data.raw here instead
return recipes
end
-- takes a recipe to search and an ingredient to find
-- returns the index in the ingredients table of the
-- ingredient or nil
function findIngredientIndex(recipe, ingredient)
for index, recipeIngredient in ipairs(recipe.ingredients) do
if table.compare(recipeIngredient, ingredient) then
return index
end
end
return nil
end
local recipe = {
type = "recipe",
name = "express-splitter",
category = "crafting-with-fluid",
enabled = "false",
energy_required = 2,
ingredients =
{
{"fast-splitter", 1},
{"iron-gear-wheel", 10},
{"advanced-circuit", 10},
{type="fluid", name="lubricant", amount=8}
},
result = "express-splitter"
}
local made = makeDuplicates(recipe, {
-- table of ingredient is key
-- it's value is a table of replacement ingredients
[{"iron-gear-wheel", 10}] = {
{"iron-ore", 20},
{"copper-ore", 50}
},
[{type="fluid", name="lubricant", amount=8}] = {
{"copper-plate", 3},
{type="fluid", name="water", amount=80}
}
})
for _, recipe in ipairs(made) do
print("\n\n"..recipe.name)
for _, ingredient in pairs(recipe.ingredients) do
local name = (ingredient[1] or ingredient.name)
local count = (ingredient[2] or ingredient.amount)
print("{"..name..", "..count.."}")
end
end
of course with lots of possible replacements it'd flood the recipe list a bit...
that is what i was dwelling on now. (i will also read and try to understand what section does what, especially the "table" part of the codes) but i have dug through some mods i have (dytech) and i think i am able to create a new menu/section and an assembler that crafts from that menu. but for each possible ingredient i think i have to create another recipe which results in a lot of (seemingly duplicate) recipes, which can create confusion, and/or beats the main purpose of the mod.
A) An inserter recipe that requires;
1 Electronic Circuit
2 Iron Plates
B) Another insterter recipe that requires;
3 Copper Cables
4 Iron Plates
C) Yet another inserter recipe that requires (that results an extra copper cable or simply destroys it);
2 Copper Plates (Or if i can find a way 1,5x Copper Plates)
4 Iron Plates
Re: using "OR" in recipes not "AND"
Posted: Sat Feb 14, 2015 6:56 pm
by FreeER
ares0027 wrote:which can create confusion, and/or beats the main purpose of the mod.
Indeed. What could also be done is to simulate a crafter 'manually' by using a regular container and Lua code. You could even have a gui to select the recipes, it's alot more work but the only 'solution' that allows for this without crowding the recipe list(s).
the basic idea would be along these lines (with onbuiltentity, onrobotbuiltentity adding a crafterInfo table to glob.crafters and with checks for the entity being valid, and a gui being used to select the recipe)
game.onevent(defines.events.ontick, function(event)
for index, crafterInfo in pairs(glob.crafters) do
if crafterInfo.recipe then
local crafter = crafterInfo.entity
local inventory = crafter.getinventory(defines.inventory.chest)
local contents = inventory.getcontents()
if checkIngredients(recipe, contents) then
removeIngredients(inventory, recipe)
insertProducts(inventory, recipe)
end
end
end
end)
function checkIngredients(recipe, contents)
for _, ingredientInfo in pairs(recipe.ingredients) do
if not contents[ingredientInfo.name] or contents[ingredientInfo.name] < ingredientInfo.amount then
return false
end
end
return true
end
function removeIngredients(inventory, recipe)
for _, ingredientInfo in pairs(recipe.ingredients) do
inventory.remove{name=ingredientInfo.name, count=ingredientInfo.amount}
end
end
function insertProducts(inventory, recipe)
for _, productInfo in pairs(recipe.products) do
inventory.insert{name=productInfo.name, count=productInfo.amount}
end
end
You could even add energy use with some extra work...
Re: using "OR" in recipes not "AND"
Posted: Mon Feb 16, 2015 12:43 pm
by ares0027
FreeER wrote:
ares0027 wrote:which can create confusion, and/or beats the main purpose of the mod.
Indeed. What could also be done is to simulate a crafter 'manually' by using a regular container and Lua code. You could even have a gui to select the recipes, it's alot more work but the only 'solution' that allows for this without crowding the recipe list(s).
the basic idea would be along these lines (with onbuiltentity, onrobotbuiltentity adding a crafterInfo table to glob.crafters and with checks for the entity being valid, and a gui being used to select the recipe)
game.onevent(defines.events.ontick, function(event)
for index, crafterInfo in pairs(glob.crafters) do
if crafterInfo.recipe then
local crafter = crafterInfo.entity
local inventory = crafter.getinventory(defines.inventory.chest)
local contents = inventory.getcontents()
if checkIngredients(recipe, contents) then
removeIngredients(inventory, recipe)
insertProducts(inventory, recipe)
end
end
end
end)
function checkIngredients(recipe, contents)
for _, ingredientInfo in pairs(recipe.ingredients) do
if not contents[ingredientInfo.name] or contents[ingredientInfo.name] < ingredientInfo.amount then
return false
end
end
return true
end
function removeIngredients(inventory, recipe)
for _, ingredientInfo in pairs(recipe.ingredients) do
inventory.remove{name=ingredientInfo.name, count=ingredientInfo.amount}
end
end
function insertProducts(inventory, recipe)
for _, productInfo in pairs(recipe.products) do
inventory.insert{name=productInfo.name, count=productInfo.amount}
end
end
You could even add energy use with some extra work...
you lost me after "a lot" part all i can do about coding is i can create a bat file to rename a file to another this was a mod request but no one showed any interest in it (i am not blaming anyone) and i have no idea about lua etc and it seems "a little" confusing for me and i really dont have time to start learning it but i will give it a try. thank you so much for all your replies.