908 lines
30 KiB
Lua
908 lines
30 KiB
Lua
require "arrays"
|
|
require "items"
|
|
require "mathhelper"
|
|
|
|
function parseIngredient(entry, toFull)
|
|
local type = entry.name and entry.name or entry[1]
|
|
local amt = entry.amount and entry.amount or entry[2]
|
|
local form = getItemType(type)
|
|
return toFull and {name=type, amount=amt, type=form} or {type, amt, form}
|
|
end
|
|
|
|
function changeRecipeTime(recipe, factor, delta)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if recipe.normal and recipe.normal.energy_required then
|
|
recipe.normal.energy_required = roundToPlaces(recipe.normal.energy_required*factor+delta, -1)
|
|
recipe.expensive.energy_required = roundToPlaces(recipe.expensive.energy_required*factor+delta, -1)
|
|
else
|
|
recipe.energy_required = roundToPlaces(recipe.energy_required*factor+delta, -1)
|
|
end
|
|
end
|
|
|
|
---@param recipe table|string
|
|
---@param item string
|
|
function getRecipeCost(recipe, item)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if recipe.normal and recipe.normal.ingredients then
|
|
local norm = -1
|
|
local exp = -1
|
|
for _,ing in pairs(recipe.normal.ingredients) do
|
|
local parse = parseIngredient(ing)
|
|
if parse[1] == item then
|
|
norm = parse[2]
|
|
break
|
|
end
|
|
end
|
|
for _,ing in pairs(recipe.expensive.ingredients) do
|
|
local parse = parseIngredient(ing)
|
|
if parse[1] == item then
|
|
exp = parse[2]
|
|
break
|
|
end
|
|
end
|
|
return {norm, exp}
|
|
elseif recipe.ingredients then
|
|
for _,ing in pairs(recipe.ingredients) do
|
|
local parse = parseIngredient(ing)
|
|
if parse[1] == item then
|
|
return parse[2]
|
|
end
|
|
end
|
|
else
|
|
log(serpent.block(recipe))
|
|
error("Recipe '" .. recipe .. "' has no ingredients?!")
|
|
end
|
|
end
|
|
|
|
function recipeStartsEnabled(recipe)
|
|
if recipe.normal then
|
|
return recipe.normal.enabled == nil or recipe.normal.enabled == true
|
|
else
|
|
return recipe.enabled == nil or recipe.enabled == true
|
|
end
|
|
end
|
|
|
|
---@return string|table
|
|
function getRecipeOutput(recipe)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found!")) end
|
|
if recipe.results then
|
|
return recipe.results
|
|
elseif recipe.result then
|
|
return recipe.result
|
|
end
|
|
if recipe.normal then
|
|
if recipe.normal.results then
|
|
return recipe.normal.results
|
|
elseif recipe.normal.result then
|
|
return recipe.normal.result
|
|
end
|
|
end
|
|
error("Recipe '" .. recipe.name .. "' has no output!")
|
|
end
|
|
|
|
---@param recipe table
|
|
---@param item string
|
|
---@return boolean
|
|
function recipeProduces(recipe, item)
|
|
--log("Checking outputs of recipe '" .. recipe.name .. "' for '" .. item .. "'")
|
|
local out = getRecipeOutput(recipe)
|
|
--log(serpent.block(out))
|
|
if type(out) == "table" then
|
|
for _,e in pairs(out) do
|
|
if listHasValue(e, item) then return true end
|
|
end
|
|
return false
|
|
else
|
|
return out == item
|
|
end
|
|
end
|
|
|
|
function convertRecipeToResultTable(recipe)
|
|
if recipe.result and not recipe.results then
|
|
recipe.results = {{type = "item", name = recipe.result, amount = recipe.result_count}}
|
|
end
|
|
if recipe.normal and recipe.normal.result and not recipe.normal.results then
|
|
recipe.normal.results = {{type = "item", name = recipe.normal.result, amount = recipe.normal.result_count}}
|
|
end
|
|
if recipe.expensive and recipe.expensive.result and not recipe.expensive.results then
|
|
recipe.expensive.results = {{type = "item", name = recipe.expensive.result, amount = recipe.expensive.result_count}}
|
|
end
|
|
end
|
|
|
|
function addRecipeProduct(recipe, item, amountnormal, amountexpensive, addIfPresent)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found!")) end
|
|
convertRecipeToResultTable(recipe)
|
|
if recipe.results then
|
|
addIngredientToList(recipe.results, item, amountnormal, addIfPresent)
|
|
end
|
|
if recipe.normal and recipe.normal.results then
|
|
addIngredientToList(recipe.normal.results, item, amountnormal, addIfPresent)
|
|
end
|
|
if recipe.expensive and recipe.expensive.results then
|
|
local amt = amountexpensive and amountexpensive or amountnormal
|
|
addIngredientToList(recipe.expensive.results, item, amt, addIfPresent)
|
|
end
|
|
if not recipe.icon and not recipe.icons then -- needs an icon when >1 output
|
|
recipe.icons = {}
|
|
|
|
local seek = data.raw.item[recipe.name]
|
|
if not seek then seek = data.raw.fluid[recipe.name] end
|
|
if seek then
|
|
table.insert(recipe.icons, {icon = seek.icon, icon_size = seek.icon_size})
|
|
else
|
|
local li = recipe.results
|
|
if not li and recipe.normal then li = recipe.normal.results end
|
|
for _,ing in pairs(li) do
|
|
if not ing.name then error("Found nil-named from: " .. serpent.block(li)) end
|
|
local val = data.raw.item[ing.name]
|
|
if not val then val = data.raw.fluid[ing.name] end
|
|
if not val then error("Product " .. ing.name .. " not indexable?!") end
|
|
table.insert(recipe.icons, {icon = val.icon, icon_size = val.icon_size})
|
|
end
|
|
end
|
|
table.insert(recipe.icons, {icon = "__DragonIndustries__/graphics/multi-recipe.png", icon_size = 32})
|
|
end
|
|
end
|
|
|
|
function turnRecipeIntoConversion(from, to)
|
|
local tgt = data.raw.recipe[to]
|
|
if not tgt then return end
|
|
local rec = createConversionRecipe(from, to, false)
|
|
tgt.ingredients = rec.ingredients
|
|
if tgt.normal then tgt.normal.ingredients = rec.normal.ingredients end
|
|
if tgt.expensive then tgt.expensive.ingredients = rec.expensive.ingredients end
|
|
end
|
|
|
|
function splitRecipeToNormalExpensive(recipe)
|
|
recipe.normal = {
|
|
enabled = recipe.enabled,
|
|
ingredients = table.deepcopy(recipe.ingredients),
|
|
results = recipe.results and table.deepcopy(recipe.results) or nil,
|
|
result = recipe.result,
|
|
energy_required = recipe.energy_required,
|
|
}
|
|
recipe.expensive = table.deepcopy(recipe.normal)
|
|
|
|
recipe.ingredients = nil
|
|
recipe.results = nil
|
|
recipe.result = nil
|
|
recipe.enabled = nil
|
|
recipe.energy_required = nil
|
|
end
|
|
|
|
---@param list table
|
|
---@param item string
|
|
---@param amount number
|
|
---@param addIfPresent? boolean
|
|
---@param catalyst? boolean
|
|
function addIngredientToList(list, item, amount, addIfPresent, catalyst)
|
|
local added = false
|
|
local itype = type(item) == "table" and item.type or "item"
|
|
local name = type(item) == "table" and item.name or item
|
|
log("Inserting recipe item " .. itype .. "/" .. name .. " x " .. amount)
|
|
for _,ing in pairs(list) do
|
|
local parse = parseIngredient(ing, true)
|
|
--log(serpent.block(parse))
|
|
if parse.name == item then
|
|
--log("Match")
|
|
if addIfPresent then
|
|
if ing.amount then
|
|
ing.amount = parse.amount+amount
|
|
else
|
|
if ing.amount then
|
|
ing.amount = parse.amount+amount
|
|
else
|
|
ing[2] = parse.amount+amount
|
|
end
|
|
end
|
|
--log("Adding " .. amount .. " to " .. serpent.block(ing))
|
|
end
|
|
added = true
|
|
break
|
|
end
|
|
end
|
|
if not added then
|
|
local add = {type = itype, name = name, amount = amount}
|
|
if catalyst then add.catalyst = catalyst end
|
|
table.insert(list, add)
|
|
end
|
|
end
|
|
|
|
function changeIngredientInList(list, item, repl, ratio, skipError)
|
|
for i = 1,#list do
|
|
local ing = parseIngredient(list[i])
|
|
--[[
|
|
if ing[1] then
|
|
log("Pos " .. i .. ": " .. ing[1] .. " x" .. ing[2] .. " for " .. item .. "->" .. repl)
|
|
else
|
|
log("Pos " .. i .. " is invalid!")
|
|
end--]]
|
|
--log("Comparing '" .. ing[1] .. "' and '" .. item .. "': " .. (ing[1] == item and "true" or "false"))
|
|
if ing[1] == item then
|
|
ing[1] = repl
|
|
ing[2] = math.ceil(ing[2]*ratio)
|
|
ing.name = repl
|
|
ing.amount = ing[2]
|
|
ing.type = ing[3]
|
|
list[i] = ing
|
|
return ing.amount
|
|
end
|
|
end
|
|
if skipError then
|
|
--log("No such item '" .. item .. "' in recipe!\n" .. debug.traceback())
|
|
return 0
|
|
else
|
|
error("No such item '" .. item .. "' in recipe!\n" .. debug.traceback())
|
|
end
|
|
end
|
|
|
|
function changeCountInList(list, item, delta, skipError)
|
|
for i = #list,1,-1 do
|
|
local ing = parseIngredient(list[i])
|
|
--[[
|
|
if ing[1] then
|
|
log("Pos " .. i .. ": " .. ing[1] .. " x" .. ing[2] .. " for " .. item .. "->" .. repl)
|
|
else
|
|
log("Pos " .. i .. " is invalid!")
|
|
end--]]
|
|
--log("Comparing '" .. ing[1] .. "' and '" .. item .. "': " .. (ing[1] == item and "true" or "false"))
|
|
if ing[1] == item then
|
|
ing[2] = ing[2]+delta
|
|
ing.name = item
|
|
ing.amount = ing[2]
|
|
ing.type = ing[3]
|
|
ing = {name = ing.name, amount = ing.amount, type = ing.type}
|
|
list[i] = ing
|
|
if ing.amount <= 0 then
|
|
table.remove(list, i)
|
|
return 0
|
|
else
|
|
return ing.amount
|
|
end
|
|
end
|
|
end
|
|
if skipError then
|
|
log("No such item '" .. item .. "' in recipe!\n" .. debug.traceback())
|
|
return 0
|
|
else
|
|
error("No such item '" .. item .. "' in recipe!\n" .. debug.traceback())
|
|
end
|
|
end
|
|
|
|
function replaceItemInRecipe(recipe, item, repl, ratio, skipError)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found! " .. debug.traceback())) end
|
|
if type(ratio) == "table" and recipe.ingredients then
|
|
splitRecipeToNormalExpensive(recipe)
|
|
end
|
|
local def, norm, exp = 0, 0, 0
|
|
local ratn = type(ratio) == "table" and ratio[1] or ratio
|
|
local rate = type(ratio) == "table" and ratio[2] or ratio
|
|
if recipe.ingredients then
|
|
def = changeIngredientInList(recipe.ingredients, item, repl, ratn, skipError)
|
|
end
|
|
if recipe.normal and recipe.normal.ingredients then
|
|
norm = changeIngredientInList(recipe.normal.ingredients, item, repl, ratn, skipError)
|
|
end
|
|
if recipe.expensive and recipe.expensive.ingredients then
|
|
exp = changeIngredientInList(recipe.expensive.ingredients, item, repl, rate, skipError)
|
|
end
|
|
log("Replaced item " .. item .. " with " .. repl .. " in recipe " .. recipe.name .. " with a ratio of " .. serpent.block(ratio) .. "x")
|
|
return {def, norm, exp}
|
|
end
|
|
|
|
function changeItemCountInRecipe(recipe, item, delta, skipError)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found! " .. debug.traceback())) end
|
|
local def, norm, exp = 0, 0, 0
|
|
if recipe.ingredients then
|
|
def = changeCountInList(recipe.ingredients, item, delta, skipError)
|
|
end
|
|
if recipe.normal and recipe.normal.ingredients then
|
|
norm = changeCountInList(recipe.normal.ingredients, item, delta, skipError)
|
|
end
|
|
if recipe.expensive and recipe.expensive.ingredients then
|
|
exp = changeCountInList(recipe.expensive.ingredients, item, delta, skipError)
|
|
end
|
|
log("Changed count of item " .. item .. " + " .. delta .. " in recipe " .. recipe.name)
|
|
--log(serpent.block(recipe))
|
|
return {def, norm, exp}
|
|
end
|
|
|
|
function removeItemFromRecipe(recipe, item)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found!")) end
|
|
if recipe.ingredients then
|
|
for i,ing in pairs(recipe.ingredients) do
|
|
if ing[1] == item then
|
|
table.remove(recipe.ingredients, i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if recipe.normal and recipe.normal.ingredients then
|
|
for i,ing in pairs(recipe.normal.ingredients) do
|
|
if ing[1] == item then
|
|
table.remove(recipe.normal.ingredients, i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if recipe.expensive and recipe.expensive.ingredients then
|
|
for i,ing in pairs(recipe.expensive.ingredients) do
|
|
if ing[1] == item then
|
|
table.remove(recipe.expensive.ingredients, i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param recipe table|string
|
|
---@param item string
|
|
---@param amountnormal number
|
|
---@param amountexpensive? number
|
|
---@param addIfPresent? boolean
|
|
---@param catalyst? boolean
|
|
function addItemToRecipe(recipe, item, amountnormal, amountexpensive, addIfPresent, catalyst)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error(serpent.block("No such recipe found!")) end
|
|
if not item then error("Tried to add null item!") end
|
|
local find = data.raw.item[item]
|
|
if not find then find = data.raw.fluid[item] end
|
|
if not find then error("No such item '" .. item .. "'!") end
|
|
log("Adding '" .. find.name .. "' x" .. amountnormal .. " to recipe '" .. recipe.name .. "'")
|
|
if recipe.ingredients then
|
|
addIngredientToList(recipe.ingredients, find, amountnormal, addIfPresent, catalyst)
|
|
end
|
|
if recipe.normal and recipe.normal.ingredients then
|
|
addIngredientToList(recipe.normal.ingredients, find, amountnormal, addIfPresent, catalyst)
|
|
end
|
|
if recipe.expensive and recipe.expensive.ingredients then
|
|
local amt = amountexpensive and amountexpensive or amountnormal
|
|
addIngredientToList(recipe.expensive.ingredients, find, amt, addIfPresent, catalyst)
|
|
end
|
|
end
|
|
|
|
---@param recipe table|string
|
|
---@param item string
|
|
---@param recipeRef string|table
|
|
---@param ratio number
|
|
---@param addIfPresent? boolean
|
|
function addRecipeIngredientToRecipe(recipe, item, recipeRef, ratio, addIfPresent)
|
|
local cost = getRecipeCost(recipeRef, item)
|
|
if cost == nil then cost = 0 end
|
|
if cost == 0 then cost = 1 end
|
|
local amountnormal = type(cost) == "table" and cost[1] or cost
|
|
local amountexpensive = type(cost) == "table" and cost[2] or cost
|
|
addItemToRecipe(recipe, item, amountnormal*ratio, amountexpensive*ratio, addIfPresent)
|
|
end
|
|
|
|
function moveRecipe(recipe, from, to)
|
|
local tech = data.raw.technology[from]
|
|
local tech2 = data.raw.technology[to]
|
|
if not tech then error("Tech '" .. from .. "' does not exist!") end
|
|
if not tech2 then error("Tech '" .. to .. "' does not exist!") end
|
|
local effects = {}
|
|
for _,effect in pairs(tech.effects) do
|
|
if effect.type == "unlock-recipe" and effect.recipe == recipe then
|
|
|
|
else
|
|
table.insert(effects, effect)
|
|
end
|
|
end
|
|
tech.effects = effects
|
|
for _,eff in pairs(tech2.effects) do
|
|
if eff.type == "unlock-recipe" and eff.recipe == recipe then return end
|
|
end
|
|
table.insert(tech2.effects, {type = "unlock-recipe", recipe = recipe})
|
|
end
|
|
|
|
function lockRecipe(recipe, from)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then return end
|
|
local tech = data.raw.technology[from]
|
|
if not tech then error("Tech '" .. from .. "' does not exist!") end
|
|
if not tech.effects then tech.effects = {} end
|
|
table.insert(tech.effects, {type = "unlock-recipe", recipe = recipe.name})
|
|
log("Putting recipe '" .. recipe.name .. "' behind tech '" .. tech.name .. "'")
|
|
recipe.enabled = false
|
|
if recipe.normal then
|
|
recipe.normal.enabled = false
|
|
end
|
|
if recipe.expensive then
|
|
recipe.expensive.enabled = false
|
|
end
|
|
end
|
|
|
|
--returns nil if none, not {}
|
|
local function buildRecipeSurplus(name1, name2, list1, list2)
|
|
local counts = {}
|
|
local ret = nil
|
|
for i = 1,#list1 do
|
|
local ing = parseIngredient(list1[i])
|
|
--log("Parsing input ingredient: " .. (ing[1] and ing[1] or "nil") .. " x " .. (ing[2] and ing[2] or "nil"))
|
|
if #ing > 0 then
|
|
--log(#ing .. " > " .. tostring(ing))
|
|
counts[ing[1]] = (counts[ing[1]] and counts[ing[1]] or 0)+(ing[2] and ing[2] or 1) -- += in case recipe specifies an ingredient multiple times
|
|
else
|
|
log("Found empty entry in recipe " .. name1 .. "!")
|
|
end
|
|
end
|
|
for i = 1,#list2 do
|
|
local ing = parseIngredient(list2[i])
|
|
--log("Parsing output ingredient: " .. (ing[1] and ing[1] or "nil") .. " x " .. (ing[2] and ing[2] or "nil"))
|
|
if #ing > 0 then
|
|
if counts[ing[1]] then
|
|
counts[ing[1]] = counts[ing[1]]-ing[2]
|
|
end
|
|
else
|
|
log("Found empty entry in recipe " .. name2 .. "!")
|
|
end
|
|
end
|
|
for item,amt in pairs(counts) do
|
|
if counts[item] > 0 and data.raw.item[item] then
|
|
if not ret then ret = {} end
|
|
ret[item] = amt
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
--Supply nil for list1 to get a plain ingredient list for list2
|
|
local function buildRecipeDifference(name1, name2, list1, list2, form, recursion)
|
|
|
|
if recursion then
|
|
for i,ing in ipairs(list1) do
|
|
--log(serpent.block(ing))
|
|
if listHasValue(list2, ing[1]) and data.raw.recipe[ing[1]] then
|
|
log("Recursing " .. ing[1])
|
|
table.remove(list1, i)
|
|
local list = form == "expensive" and data.raw.recipe[ing[1]].expensive.ingredients or ("normal" and data.raw.recipe[ing[1]].normal.ingredients or data.raw.recipe[ing[1]].ingredients)
|
|
for _,e in pairs(buildRecipeDifference("", ing[1], nil, list)) do
|
|
table.insert(list1, e)
|
|
log("Adding " .. e[1])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not list2 then error(debug.traceback()) end
|
|
|
|
local counts = {}
|
|
local ret = {}
|
|
if list1 then
|
|
for i = 1,#list1 do
|
|
local ing = parseIngredient(list1[i])
|
|
--log("Parsing input ingredient: " .. (ing[1] and ing[1] or "nil") .. " x " .. (ing[2] and ing[2] or "nil"))
|
|
if #ing > 0 and ing[1] then
|
|
--log(#ing .. " > " .. tostring(ing))
|
|
counts[ing[1]] = (counts[ing[1]] and counts[ing[1]] or 0)+(ing[2] and ing[2] or 1) -- += in case recipe specifies an ingredient multiple times
|
|
else
|
|
log("Found empty entry in recipe " .. name1 .. "!")
|
|
end
|
|
end
|
|
end
|
|
for i = 1,#list2 do
|
|
local ing = parseIngredient(list2[i])
|
|
--log("Parsing output ingredient: " .. (ing[1] and ing[1] or "nil") .. " x " .. (ing[2] and ing[2] or "nil"))
|
|
if #ing > 0 and ing[1] then
|
|
local amt = ing[2]-(counts[ing[1]] and counts[ing[1]] or 0)
|
|
if amt > 0 then
|
|
ret[#ret+1] = {ing[1], amt}
|
|
end
|
|
else
|
|
log("Found empty entry in recipe " .. name2 .. "!")
|
|
end
|
|
end
|
|
|
|
for i = #ret,1,-1 do
|
|
local e = ret[i]
|
|
if e == nil or e[1] == nil then
|
|
log("Slot #" .. i .. " in the recipe difference between '" .. name1 .. "' and '" .. name2 .. "' was nil or nil-named!")
|
|
table.remove(ret, i)
|
|
end
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
---@param recipe table
|
|
local function createRecipeProfile(recipe)
|
|
return
|
|
{
|
|
enabled = recipe.enabled,
|
|
ingredients = table.deepcopy(recipe.ingredients),
|
|
result = recipe.result,
|
|
results = recipe.results and table.deepcopy(recipe.results) or nil,
|
|
}
|
|
end
|
|
|
|
---@param recipe table|string
|
|
local function swapRecipeIO(recipe)
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then return end
|
|
local temp = table.deepcopy(recipe.ingredients)
|
|
for _,e in pairs(temp) do
|
|
if not e.name then
|
|
e.name = e[1]
|
|
e.amount = e[2]
|
|
e.type = "item"
|
|
end
|
|
end
|
|
local result = recipe.results
|
|
if not result then
|
|
result = {{type = "item", name = recipe.result, amount = recipe.result_count and recipe.result_count or 1}}
|
|
end
|
|
recipe.ingredients = table.deepcopy(result)
|
|
recipe.results = temp
|
|
end
|
|
|
|
---@param recipe table
|
|
---@param ref table
|
|
function createUncraftingRecipe(recipe, ref)
|
|
local ret = table.deepcopy(recipe)
|
|
swapRecipeIO(ret)
|
|
ret.name = recipe.name .. "-uncraft"
|
|
ret.icons = {{icon = ref.icon, icon_size = ref.icon_size}, {icon = "__DragonIndustries__/graphics/icons/uncrafting_overlay.png", icon_size = 32}}
|
|
ret.subgroup = ref.subgroup
|
|
ret.allow_decomposition = false
|
|
ret.allow_as_intermediate = false
|
|
ret.allow_intermediates = false
|
|
ret.localised_name = {"uncraft-recipe.name", {"entity-name." .. ref.name}}
|
|
return ret
|
|
end
|
|
|
|
---@param from string|table
|
|
---@param to string|table
|
|
---@param register? boolean
|
|
---@param tech? string|table
|
|
---@param recursion? table
|
|
function createConversionRecipe(from, to, register, tech, recursion)
|
|
local rec1 = type(from) == "table" and from or data.raw.recipe[from]
|
|
local rec2 = type(to) == "table" and from or data.raw.recipe[to]
|
|
if tech and type(tech) == "table" then tech = tech.name end
|
|
|
|
if not rec1 then
|
|
error("No such recipe '" .. from .. "'!")
|
|
end
|
|
if not rec2 then
|
|
error("No such recipe '" .. to .. "'!")
|
|
end
|
|
|
|
rec1 = table.deepcopy(rec1)
|
|
rec2 = table.deepcopy(rec2)
|
|
|
|
local name = rec1.name .. "-conversion-to-" .. rec2.name
|
|
|
|
if data.raw.recipe.name then
|
|
error("Conversion recipe already exists: " .. name)
|
|
else
|
|
log("Creating conversion recipe " .. name)
|
|
if recursion then
|
|
log("Recursing ingredients " .. serpent.block(recursion))
|
|
end
|
|
end
|
|
|
|
local list = nil
|
|
local exp = nil
|
|
local norm = nil
|
|
|
|
local e_list = nil
|
|
local e_exp = nil
|
|
local e_norm = nil
|
|
|
|
if rec1.normal and not rec2.normal then --harmonize the recipe styles
|
|
rec2.normal = createRecipeProfile(rec2)
|
|
rec2.expensive = createRecipeProfile(rec2)
|
|
elseif rec2.normal and not rec1.normal then
|
|
rec1.normal = createRecipeProfile(rec1)
|
|
rec1.expensive = createRecipeProfile(rec1)
|
|
end
|
|
|
|
local prev = rec1.expensive and rec1.expensive.result or rec1.result
|
|
if not prev then
|
|
local list = rec1.results and rec1.results or rec1.expensive.results --[[@as table]]
|
|
for _,res in pairs(list) do
|
|
if res.type == "item" then
|
|
prev = res.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if rec1.ingredients and rec2.ingredients then
|
|
list = buildRecipeDifference(from, to, rec1.ingredients, rec2.ingredients, "basic", recursion)
|
|
e_list = buildRecipeSurplus(from, to, rec1.ingredients, rec2.ingredients)
|
|
end
|
|
|
|
if rec1.expensive or rec2.expensive then
|
|
local exp1 = rec1.expensive and rec1.expensive.ingredients or rec1.ingredients
|
|
local exp2 = rec2.expensive and rec2.expensive.ingredients or rec2.ingredients
|
|
exp = buildRecipeDifference(from, to, exp1, exp2, "expensive", recursion)
|
|
e_exp = buildRecipeSurplus(from, to, exp1, exp2)
|
|
end
|
|
|
|
if rec1.normal or rec2.normal then
|
|
local norm1 = rec1.normal and rec1.normal.ingredients or rec1.ingredients
|
|
local norm2 = rec2.normal and rec2.normal.ingredients or rec2.ingredients
|
|
norm = buildRecipeDifference(from, to, norm1, norm2, "normal", recursion)
|
|
e_norm = buildRecipeSurplus(from, to, norm1, norm2)
|
|
end
|
|
|
|
if prev then
|
|
if list then
|
|
table.insert(list, {prev, rec1.result_count and rec1.result_count or 1})
|
|
end
|
|
if exp then
|
|
table.insert(exp, {prev, rec1.expensive.result_count and rec1.expensive.result_count or 1})
|
|
end
|
|
if norm then
|
|
table.insert(norm, {prev, rec1.normal.result_count and rec1.normal.result_count or 1})
|
|
end
|
|
end
|
|
|
|
local ret = table.deepcopy(rec2)
|
|
ret.name = name
|
|
ret.ingredients = list
|
|
local main = rec1.result and rec1.result or rec1.normal.result
|
|
local result = rec2.result and rec2.result or rec2.normal.result
|
|
|
|
if main == nil then
|
|
local rec1results = rec1.results and rec1.results or rec1.normal.results
|
|
if rec1results == nil then
|
|
error("Recipe '" .. rec1.name .. "' has neither a result on the recipe nor its cost subrecipe!" .. serpent.block(rec1))
|
|
end
|
|
for _,ing in pairs(rec1results) do
|
|
if ing.type == "item" then
|
|
main = ing.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if result == nil then
|
|
local rec2results = rec2.results and rec2.results or rec2.normal.results
|
|
if rec2results == nil then
|
|
error("Recipe '" .. rec1.name .. "' has neither a result on the recipe nor its cost subrecipe!" .. serpent.block(rec1))
|
|
end
|
|
for _,ing in pairs(rec2results) do
|
|
if ing.type == "item" then
|
|
result = ing.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not main then
|
|
error("Cannot create a conversion recipe from a recipe that has no output!" .. serpent.block(rec1))
|
|
end
|
|
if not result then
|
|
error("Cannot create a conversion recipe to a recipe that has no output!" .. serpent.block(rec2))
|
|
end
|
|
|
|
local out = getItemByName(result)
|
|
|
|
if not out then
|
|
error("Cannot create a conversion recipe to an item that does not exist!" .. serpent.block(rec2))
|
|
end
|
|
|
|
local itemType = out.type
|
|
|
|
ret.localised_name = {"conversion-recipe.name", {"entity-name." .. main}, {"entity-name." .. result}}
|
|
local orig_icon_src = rec2
|
|
if (not (orig_icon_src.icon or orig_icon_src.icons)) and data.raw[itemType][result] then
|
|
orig_icon_src = data.raw[itemType][result]
|
|
end
|
|
if not (orig_icon_src and (orig_icon_src.icon or orig_icon_src.icons)) then
|
|
error("Could not find an icon for " .. rec2.name .. ", in either the recipe or its produced item! This item is invalid and would have crashed the game anyways!")
|
|
end
|
|
local ico = orig_icon_src.icon and orig_icon_src.icon or orig_icon_src.icons[1].icon
|
|
local icosz = orig_icon_src.icon_size and orig_icon_src.icon_size or orig_icon_src.icons[1].icon_size
|
|
ret.icons = {{icon = ico, icon_size = icosz}, {icon = "__DragonIndustries__/graphics/icons/conversion_overlay.png", icon_size = 32}}
|
|
if not ret.icon then
|
|
if data.raw[itemType][result] then
|
|
ret.icon = data.raw[itemType][result].icon
|
|
else
|
|
log("Could not create icon for conversion recipe '" .. name .. "'! No such item '" .. result .. "'")
|
|
end
|
|
end
|
|
if e_list then
|
|
local out = ret.result
|
|
if out == nil then
|
|
for _,ing in pairs(ret.results) do
|
|
if ing.type == "item" then
|
|
out = ing.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
ret.results = {{type = "item", name = out, amount = ret.result_count and ret.result_count or 1}}
|
|
for type,count in pairs(e_list) do
|
|
if not type then
|
|
log("Found a nameless entry in a recipe result/ingredient list when generating a conversion recipe?")
|
|
else
|
|
table.insert(ret.results, {type = "item", name = type, amount = count})
|
|
end
|
|
end
|
|
ret.result = nil
|
|
ret.subgroup = data.raw[itemType][result].subgroup
|
|
end
|
|
if ret.normal then
|
|
ret.normal.ingredients = norm
|
|
if e_norm then
|
|
local out = ret.normal.result
|
|
if out == nil then
|
|
for _,ing in pairs(ret.normal.results) do
|
|
if ing.type == "item" then
|
|
out = ing.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
ret.normal.results = {{type = "item", name = out, amount = ret.normal.result_count and ret.normal.result_count or 1}}
|
|
for type,count in pairs(e_norm) do
|
|
if not type then
|
|
log("Found a nameless entry in a recipe result/ingredient list when generating a conversion recipe?")
|
|
else
|
|
table.insert(ret.normal.results, {type = "item", name = type, amount = count})
|
|
end
|
|
end
|
|
ret.normal.result = nil
|
|
ret.subgroup = data.raw[itemType][result].subgroup
|
|
end
|
|
end
|
|
if ret.expensive then
|
|
ret.expensive.ingredients = exp
|
|
if e_exp then
|
|
local out = ret.expensive.result
|
|
if out == nil then
|
|
for _,ing in pairs(ret.expensive.results) do
|
|
if ing.type == "item" then
|
|
out = ing.name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
ret.expensive.results = {{type = "item", name = out, amount = ret.expensive.result_count and ret.expensive.result_count or 1}}
|
|
for type,count in pairs(e_exp) do
|
|
if not type then
|
|
log("Found a nameless entry in a recipe result/ingredient list when generating a conversion recipe?")
|
|
else
|
|
table.insert(ret.expensive.results, {type = "item", name = type, amount = count})
|
|
end
|
|
end
|
|
ret.expensive.result = nil
|
|
ret.subgroup = data.raw[itemType][result].subgroup
|
|
end
|
|
end
|
|
|
|
if data.raw.item["basic-circuit-board"] then
|
|
replaceItemInRecipe(ret, "electronic-circuit", "basic-circuit-board", 1, true)
|
|
end
|
|
|
|
ret.allow_decomposition = false
|
|
ret.allow_as_intermediate = false
|
|
ret.allow_intermediates = false
|
|
if ret.normal then
|
|
ret.normal.allow_decomposition = false
|
|
ret.normal.allow_as_intermediate = false
|
|
ret.normal.allow_intermediates = false
|
|
end
|
|
if ret.expensive then
|
|
ret.expensive.allow_decomposition = false
|
|
ret.expensive.allow_as_intermediate = false
|
|
ret.expensive.allow_intermediates = false
|
|
end
|
|
|
|
if ret.ingredients == nil and (ret.normal == nil or ret.normal.ingredients == nil) then error("Conversion recipe " .. ret.name .. " has no specified ingredients! Source recipes: " .. serpent.block(rec1) .. " , " .. serpent.block(rec2)) end
|
|
if ret.ingredients then
|
|
for _,ing in pairs(ret.ingredients) do
|
|
if ing[2] == 0 then
|
|
error("Conversion recipe " .. ret.name .. " has no 0-count ingredients! Source recipes: " .. serpent.block(rec1) .. " , " .. serpent.block(rec2))
|
|
end
|
|
end
|
|
end
|
|
if ret.normal and ret.normal.ingredients then
|
|
for _,ing in pairs(ret.normal.ingredients) do
|
|
if ing[2] == 0 then
|
|
error("Conversion recipe " .. ret.name .. " has no 0-count ingredients! Source recipes: " .. serpent.block(rec1) .. " , " .. serpent.block(rec2))
|
|
end
|
|
end
|
|
end
|
|
|
|
if register then
|
|
data:extend({ret})
|
|
|
|
if tech then
|
|
table.insert(data.raw.technology[tech].effects, {type = "unlock-recipe", recipe = name})
|
|
end
|
|
end
|
|
|
|
--log(serpent.block(ret))
|
|
|
|
return ret
|
|
end
|
|
|
|
function streamlineRecipeOutputWithRecipe(recipe, with, main, skipError)
|
|
log("Streamlining '" .. recipe.name .. "' with recipe '" .. with .. "' for main item '" .. main .. "'")
|
|
if not recipe.results then error("You can only output-streamline multi-output recipes!") end
|
|
local stream = data.raw.recipe[with]
|
|
if not stream then error("Recipe '" .. with .. "' does not exist!") end
|
|
local extras = {}
|
|
local extraAmt = {}
|
|
local amt = -1
|
|
for _,ing in pairs(recipe.results) do
|
|
local parse = parseIngredient(ing)
|
|
if parse[1] == main then
|
|
amt = parse[2]
|
|
else
|
|
table.insert(extras, parse[1])
|
|
extraAmt[parse[1]] = parse[2]
|
|
end
|
|
end
|
|
log("Extras: " .. serpent.block(extraAmt))
|
|
local need = stream.ingredients
|
|
if not need and stream.normal then need = stream.normal.ingredients end
|
|
if not need then error("Recipe '" .. with .. "' has no ingredients!") end
|
|
log("Total needed: " .. serpent.block(need))
|
|
need = table.deepcopy(need)
|
|
for i=#need,1,-1 do
|
|
local ing = need[i]
|
|
local parse = parseIngredient(ing)
|
|
if listHasValue(extras, parse[1]) then
|
|
local ext = extraAmt[parse[1]]
|
|
if parse[2] <= ext then
|
|
log("Output contained sufficient extra. Removing from need.")
|
|
table.remove(need, i)
|
|
table.remove(recipe.results, i)
|
|
else
|
|
log("Output did not contain sufficient extra. Removing only " .. ext .. " from need of " .. parse[2] .. ".")
|
|
extraAmt[parse[1]] = 0
|
|
parse[2] = parse[2]-ext
|
|
if ing.amount then
|
|
ing.amount = parse[2]
|
|
recipe.results[i].amount = ing.amount
|
|
else
|
|
ing[2] = parse[2]
|
|
recipe.results[i][2] = ing[2]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--log("Diff needed: " .. serpent.block(need))
|
|
for _,ing in pairs(need) do
|
|
local parse = parseIngredient(ing)
|
|
local amt = parse[2]
|
|
addItemToRecipe(recipe, parse[1], amt, amt, true)
|
|
end
|
|
changeItemCountInRecipe(recipe, with, -1, skipError)
|
|
--log(serpent.block(recipe))
|
|
end
|
|
|
|
---@param item string|table
|
|
function markItemForProductivityAllowed(item)
|
|
if type(item) == "string" then item = getItemByName(item) end
|
|
if not item then error("No such item '" .. item .. "'") end
|
|
log("Adding item '" .. item.name .. "' to the valid-with-productivity list")
|
|
for name,recipe in pairs(data.raw.recipe) do
|
|
if recipeProduces(recipe, item.name) then
|
|
markForProductivityAllowed(recipe)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param recipe string|table
|
|
function markForProductivityAllowed(recipe)
|
|
local arg = recipe
|
|
if type(recipe) == "string" then recipe = data.raw.recipe[recipe] end
|
|
if not recipe then error("No such recipe '" .. arg .. "'") end
|
|
--if not recipe.allow_as_intermediate then log("Skipping productivity allowance on uncraft/upgrade recipe to prevent exploits: " .. recipe.name) return end
|
|
for name,module in pairs(data.raw.module) do
|
|
if module.effect and module.effect["productivity"] and module.limitation and getTableSize(module.limitation) > 0 then
|
|
log("Adding recipe '" .. recipe.name .. "' to the valid-with-productivity list on '" .. name .. "'")
|
|
table.insert(module.limitation, recipe.name)
|
|
end
|
|
end
|
|
end |