Rewrite recipe code and add group support (#338)

This commit is contained in:
OgelGames 2024-01-14 15:03:32 +11:00 committed by GitHub
parent e77a6715ce
commit a280a1429e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 201 additions and 135 deletions

View File

@ -30,7 +30,7 @@ read_globals = {
"craftguide", "i3", "mtt",
"vizlib", "mcl_sounds", "mcl_vars",
"mcl_worlds", "mcl_buckets", "mcl_formspec",
"mcl_craftguide","exchangeclone",
"mcl_craftguide",
-- Only used in technic/machines/MV/lighting.lua (disabled)
"isprotect", "homedecor_expect_infinite_stacks",

View File

@ -46,29 +46,15 @@ local function register_tree_grinding(name, tree, wood, extract, grinding_color)
end
end
local rubber_tree_planks = moretrees and "moretrees:rubber_tree_planks"
local rubber_planks = moretrees and "moretrees:rubber_tree_planks"
local default_extract = dye and "dye:brown 2"
-- https://en.wikipedia.org/wiki/Catechu ancient brown dye from the wood of acacia trees
local acacia_extract = dye and "dye:brown 8"
-- technic recipes don't support groups yet :/
--register_tree_grinding("Common Tree", "group:tree", "group:wood", default_extract)
-- Specific recipes for acacia and rubber trees
register_tree_grinding("Acacia", mat.acacia_tree, mat.acacia_wood, acacia_extract)
register_tree_grinding("Common Tree", mat.tree, mat.wood, default_extract)
register_tree_grinding("Common Tree", mat.aspen_tree, mat.aspen_wood, default_extract)
register_tree_grinding("Common Tree", mat.jungletree, mat.junglewood, default_extract)
register_tree_grinding("Common Tree", mat.pine_tree, mat.pine_wood, default_extract)
register_tree_grinding("Rubber Tree", "moretrees:rubber_tree_trunk", rubber_tree_planks, "technic:raw_latex")
register_tree_grinding("Rubber Tree", "moretrees:rubber_tree_trunk", rubber_planks, "technic:raw_latex 2")
register_tree_grinding("Rubber Tree", "moretrees:rubber_tree_trunk_empty", nil, "technic:raw_latex")
if moretrees then
local trees = {
"beech", "apple_tree", "oak", "sequoia", "birch", "palm",
"date_palm", "spruce", "cedar", "poplar", "willow", "fir"
}
for _,tree in pairs(trees) do
register_tree_grinding("Common Tree", "moretrees:"..tree.."_trunk", "moretrees:"..tree.."_planks", default_extract)
end
end
-- Group recipe for all other trees
register_tree_grinding("Common Tree", "group:tree", "group:wood", default_extract)

View File

@ -4,174 +4,260 @@ local have_cg = minetest.get_modpath("craftguide")
local have_mcl_cg = minetest.get_modpath("mcl_craftguide")
local have_i3 = minetest.get_modpath("i3")
technic.recipes = { cooking = { input_size = 1, output_size = 1 } }
technic.recipes = {
cooking = {input_size = 1, output_size = 1, recipes = {}},
}
function technic.register_recipe_type(typename, origdata)
local data = table.copy(origdata)
local temp_recipes = {} -- Used to store recipes before caching
local recipe_cache = {} -- Cache used by technic.get_recipe
function technic.register_recipe_type(method, data)
data = table.copy(data)
data.input_size = data.input_size or 1
data.output_size = data.output_size or 1
if have_ui and unified_inventory.register_craft_type then
unified_inventory.register_craft_type(typename, {
data.recipes = {}
if have_ui then
unified_inventory.register_craft_type(method, {
description = data.description,
icon = data.icon,
width = data.input_size,
height = 1,
})
end
if have_cg and craftguide.register_craft_type then
craftguide.register_craft_type(typename, {
if have_cg then
craftguide.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
if have_mcl_cg then
mcl_craftguide.register_craft_type(typename, {
mcl_craftguide.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
if have_i3 then
i3.register_craft_type(typename, {
i3.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
data.recipes = {}
technic.recipes[typename] = data
technic.recipes[method] = data
end
local function get_recipe_index(items)
if not items or type(items) ~= "table" then return false end
local l = {}
function technic.register_recipe(method, data)
data.time = data.time or 1
data.method = method
if type(data.input) == "string" then
data.input = {data.input}
end
if type(data.output) == "string" then
data.output = {data.output}
end
table.insert(temp_recipes, data)
end
local function get_recipe_key(method, items)
local t = {}
for i, stack in ipairs(items) do
l[i] = ItemStack(stack):get_name()
t[i] = ItemStack(stack):get_name()
end
table.sort(l)
return table.concat(l, "/")
table.sort(t)
return method.."/"..table.concat(t, "/")
end
local function register_recipe(typename, data)
-- Handle aliases
for i, stack in ipairs(data.input) do
data.input[i] = ItemStack(stack):to_string()
end
if type(data.output) == "table" then
for i, v in ipairs(data.output) do
data.output[i] = ItemStack(data.output[i]):to_string()
end
else
data.output = ItemStack(data.output):to_string()
end
local recipe = {time = data.time, input = {}, output = data.output}
local index = get_recipe_index(data.input)
if not index then
minetest.log("warning", "[Technic] ignored registration of garbage recipe!")
function technic.get_recipe(method, items)
local key = get_recipe_key(method, items)
local recipe = recipe_cache[key]
if not recipe then
return
end
for _, stack in ipairs(data.input) do
recipe.input[ItemStack(stack):get_name()] = ItemStack(stack):get_count()
local new_input = {}
for i, stack in ipairs(items) do
local amount = recipe.input[stack:get_name()]
if stack:get_count() < amount then
return
else
new_input[i] = ItemStack(stack)
new_input[i]:take_item(amount)
end
end
return {
time = recipe.time,
new_input = new_input,
output = recipe.output
}
end
technic.recipes[typename].recipes[index] = recipe
if data.hidden then return end
local outputs = type(data.output) == "table" and data.output or {data.output}
for _,output in ipairs(outputs) do
local function add_to_craftguides(recipe)
for _, output in ipairs(recipe.output) do
if have_ui then
unified_inventory.register_craft({
type = typename,
type = recipe.method,
output = output,
items = data.input,
items = table.copy(recipe.input),
width = 0,
})
end
if have_cg and craftguide.register_craft then
craftguide.register_craft({
type = typename,
type = recipe.method,
result = output,
items = {table.concat(data.input, ", ")},
items = {table.concat(recipe.input, ", ")},
})
end
if have_mcl_cg then
mcl_craftguide.register_craft({
type = typename,
type = recipe.method,
output = output,
items = data.input,
items = table.copy(recipe.input),
width = 0,
})
end
if have_i3 then
i3.register_craft({
type = typename,
type = recipe.method,
result = output,
items = {table.concat(data.input, ", ")},
items = {table.concat(recipe.input, ", ")},
})
end
end
end
-- Checks for "zzzz_exchangeclone_crafthook" mod so that it won't crash with older versions of ExchangeClone
-- which don't have it.
local has_exchangeclone = minetest.get_modpath("zzzz_exchangeclone_init")
function technic.register_recipe(typename, data)
if has_exchangeclone then
exchangeclone.register_technic_recipe(typename, data)
end
if have_mcl_cg then
register_recipe(typename, data)
else
minetest.after(0.01, register_recipe, typename, data) -- Handle aliases
end
end
function technic.get_recipe(typename, items)
if typename == "cooking" then -- Already built into Minetest, so use that
local result, new_input = minetest.get_craft_result({
method = "cooking",
width = 1,
items = items,
})
-- Compatibility layer
if not result or result.time == 0 then
return
-- Workaround for recipes with replacements
elseif not new_input.items[1]:is_empty() and new_input.items[1]:get_name() ~= items[1]:get_name() then
items[1]:take_item(1)
return {
time = result.time,
new_input = {items[1]},
output = {new_input.items[1], result.item}
}
else
return {
time = result.time,
new_input = new_input.items,
output = result.item
}
end
end
local index = get_recipe_index(items)
if not index then return end
local recipe = technic.recipes[typename].recipes[index]
if recipe then
local new_input = {}
for i, stack in ipairs(items) do
if stack:get_count() < recipe.input[stack:get_name()] then
return
else
new_input[i] = ItemStack(stack)
new_input[i]:take_item(recipe.input[stack:get_name()])
local function get_items_in_group(group)
local items = {}
local groups = group:split(",")
for name, def in pairs(minetest.registered_items) do
local match = true
for _,g in pairs(groups) do
if not def.groups[g] then
match = false
break
end
end
return {
time = recipe.time,
new_input = new_input,
output = recipe.output
}
else
if match then
items[#items+1] = name
end
end
return items
end
local function get_recipe_variants(items, index)
index = index or 1
if not items[index] then
return
end
local list = {}
local variants = get_recipe_variants(items, index + 1)
if variants then
for _,a in pairs(items[index]) do
for _,b in pairs(variants) do
list[#list+1] = a..","..b
end
end
else
for _,a in pairs(items[index]) do
list[#list+1] = a
end
end
if index == 1 then
for i, str in pairs(list) do
list[i] = str:split(",")
end
end
return list
end
local function cache_recipe(data)
-- Create the basic recipe
local recipe = {time = data.time, input = {}, output = {}}
for _, item in ipairs(data.input) do
if item:match("^group:") then
local split = item:split(" ")
recipe.input[split[1]] = tonumber(split[2]) or 1
else
local stack = ItemStack(item)
recipe.input[stack:get_name()] = stack:get_count()
end
end
for i, item in ipairs(data.output) do
recipe.output[i] = ItemStack(item):to_string()
end
if data.method ~= "cooking" then
table.insert(technic.recipes[data.method].recipes, recipe)
end
-- Find all unique variants of the recipe and cache them
-- If there are no group items, there will only be one
local all_items, item_counts = {}, {}
local has_group_item = false
for item, count in pairs(recipe.input) do
local group = item:match("^group:(.+)$")
if group then
table.insert(all_items, get_items_in_group(group))
has_group_item = true
else
table.insert(all_items, {ItemStack(item):get_name()})
end
table.insert(item_counts, count)
end
if not has_group_item then
local key = get_recipe_key(data.method, data.input)
recipe_cache[key] = table.copy(recipe)
return
end
for _,items in pairs(get_recipe_variants(all_items)) do
local key = get_recipe_key(data.method, items)
-- Non-group recipes take priority over group recipes
if not has_group_item or not recipe_cache[key] then
local input = {}
for i, item in ipairs(items) do
input[item] = item_counts[i]
end
recipe_cache[key] = {
time = data.time,
input = input,
output = table.copy(recipe.output),
}
end
end
end
local function cache_all_recipes()
-- Cache built in cooking recipes
for item in pairs(minetest.registered_items) do
local recipes = minetest.get_all_craft_recipes(item)
for _,recipe in ipairs(recipes or {}) do
if recipe.method == "cooking" then
local result, new_input = minetest.get_craft_result(recipe)
if result and result.time > 0 then
local data = {
method = "cooking",
time = result.time,
input = recipe.items,
output = {result.item:to_string()},
}
local replacement = new_input.items[1]
if not replacement:is_empty() then
data.output[2] = replacement:to_string()
end
cache_recipe(data)
end
end
end
end
-- Cache custom recipes
for _, data in pairs(temp_recipes) do
if not data.hidden then
add_to_craftguides(data)
end
cache_recipe(data)
end
temp_recipes = nil
end
-- Slightly hacky way to be the first function called
table.insert(minetest.registered_on_mods_loaded, 1, cache_all_recipes)
minetest.callback_origins[cache_all_recipes] = {
mod = "technic",
name = "register_on_mods_loaded",
}

View File

@ -60,12 +60,6 @@ technic.materials = {
wood = has_mcl and "mcl_core:wood" or "default:wood",
acacia_tree = has_mcl and "mcl_core:acaciatree" or "default:acacia_tree",
acacia_wood = has_mcl and "mcl_core:acaciawood" or "default:acacia_wood",
aspen_tree = has_mcl and "mcl_core:birchtree" or "default:aspen_tree",
aspen_wood = has_mcl and "mcl_core:birchwood" or "default:aspen_wood",
jungletree = has_mcl and "mcl_core:jungletree" or "default:jungletree",
junglewood = has_mcl and "mcl_core:junglewood" or "default:junglewood",
pine_tree = has_mcl and "mcl_core:sprucetree" or "default:pine_tree",
pine_wood = has_mcl and "mcl_core:sprucewood" or "default:pine_wood",
water_source = has_mcl and "mcl_core:water_source" or "default:water_source",
water_flowing = has_mcl and "mcl_core:water_flowing" or "default:water_flowing",
river_water_source = has_mcl and "mclx_core:river_water_source" or "default:river_water_source",

View File

@ -1,3 +1,3 @@
name = technic
depends = pipeworks, technic_worldgen, basic_materials
optional_depends = mcl_core, mcl_sounds, default, bucket, mesecons, mesecons_mvps, digilines, digiline_remote, unified_inventory, dye, craftguide, i3, mtt, vizlib, moreores, mcl_buckets, mcl_explosions, mcl_craftguide, zzzz_exchangeclone_init
optional_depends = mcl_core, mcl_sounds, default, bucket, mesecons, mesecons_mvps, digilines, digiline_remote, unified_inventory, dye, craftguide, i3, mtt, vizlib, moreores, mcl_buckets, mcl_explosions, mcl_craftguide