Rewrite recipe code and add group support (#338)
This commit is contained in:
parent
e77a6715ce
commit
a280a1429e
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user