4db64944c9
Right now it can traverse the craft tree and construct a crafting queue.
265 lines
6.1 KiB
Lua
265 lines
6.1 KiB
Lua
|
|
-- TODO
|
|
--[[
|
|
free space could be a concern
|
|
traverse_recurse should replace groups with the concrete item
|
|
maybe recursions yield an item name?
|
|
that way has_sub could indicate the item which would be yielded in traverse_recurse and replaced in the recipe before being queued
|
|
autocraft.recipes should be loaded and custom per modset
|
|
it might choke on empty items in the recipe
|
|
this needs to handle groups a bit better, i dont think it will mix woods/etc
|
|
--]]
|
|
|
|
autocraft = {}
|
|
|
|
|
|
autocraft.recipes = {
|
|
["mcl_core:crafting_table"] = {
|
|
recipes = {
|
|
{
|
|
recipe = {
|
|
{"group:planks", "group:planks"},
|
|
{"group:planks", "group:planks"}}
|
|
}
|
|
}
|
|
},
|
|
["mcl_core:wood"] = {
|
|
groups = {"planks"},
|
|
recipes = {
|
|
{
|
|
count = 4,
|
|
recipe = {
|
|
"mcl_core:tree"
|
|
},
|
|
shapeless = true -- redundant, can detect from the lack of subtables
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
autocraft.groups = {}
|
|
|
|
-- extract groups from autocraft.recipes
|
|
local function group_arrange()
|
|
for k, v in pairs(autocraft.recipes) do
|
|
if v.groups then
|
|
for gi, gv in ipairs(v.groups) do
|
|
if not autocraft.groups[gv] then
|
|
autocraft.groups[gv] = {}
|
|
end
|
|
|
|
table.insert(autocraft.groups[gv], k)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
group_arrange()
|
|
|
|
local function startswith(str, start)
|
|
return string.sub(str, 1, #start) == start
|
|
end
|
|
|
|
local function combine(t1, t2)
|
|
local t1l = #t1
|
|
local o = {}
|
|
|
|
for i, v in ipairs(t1) do
|
|
o[i] = v
|
|
end
|
|
|
|
for i, v in ipairs(t2) do
|
|
o[t1l + i] = v
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
local function parse_group(str)
|
|
if startswith(str, "group:") then
|
|
return str:match("group:(.+)")
|
|
end
|
|
end
|
|
|
|
-- get recipes for an item/group
|
|
local function get_recipes(str)
|
|
local group = parse_group(str)
|
|
|
|
if group then
|
|
local o = {}
|
|
|
|
if autocraft.groups[group] then
|
|
for i, v in ipairs(autocraft.groups[group]) do
|
|
o = combine(o, get_recipes(v))
|
|
end
|
|
|
|
return o
|
|
end
|
|
else
|
|
local idef = autocraft.recipes[str]
|
|
if idef then
|
|
local o = idef.recipes
|
|
|
|
for i, v in ipairs(o) do
|
|
v.name = str
|
|
end
|
|
|
|
return o
|
|
end
|
|
end
|
|
|
|
return {}
|
|
end
|
|
|
|
-- count up all the items in the player's inventory
|
|
-- output:
|
|
-- {
|
|
-- item = n,
|
|
-- item = n
|
|
-- }
|
|
local function count_inv()
|
|
local o = {}
|
|
local lpim = minetest.get_inventory("current_player").main
|
|
|
|
for i, v in ipairs(lpim) do
|
|
if not v:is_empty() then
|
|
o[v:get_name()] = (o[v:get_name()] or 0) + v:get_count()
|
|
end
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
-- effectively turn a recipe shapeless
|
|
local function flatten_recipe(recipe)
|
|
if type(recipe[1]) == "table" then
|
|
local o = {}
|
|
|
|
for i, v in ipairs(recipe) do
|
|
o = combine(o, v)
|
|
end
|
|
|
|
return o
|
|
else
|
|
return recipe
|
|
end
|
|
end
|
|
|
|
-- count the requirements for a recipe, uses count_inv format
|
|
local function count_recipe(recipe)
|
|
local o = {}
|
|
|
|
for i, v in ipairs(flatten_recipe(recipe)) do
|
|
local item = ItemStack(v)
|
|
o[item:get_name()] = (o[item:get_name()] or 0) + item:get_count()
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
-- get all item strings for an item or group string
|
|
local function get_items_of(str)
|
|
local group = parse_group(str)
|
|
|
|
if group then
|
|
return autocraft.groups[group]
|
|
else
|
|
return {str}
|
|
end
|
|
end
|
|
|
|
-- check if the recipe can be crafted with current resources
|
|
local function can_craft(resources, recipe)
|
|
for k, count in pairs(count_recipe(recipe)) do
|
|
for i, vv in ipairs(get_items_of(k)) do
|
|
local item = ItemStack(vv)
|
|
if (resources[item:get_name()] or 0) >= count then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- traverse all items in a recipe
|
|
local function recurse_recipe(resources, queue, recipe)
|
|
for i, v in ipairs(flatten_recipe(recipe)) do
|
|
if not autocraft.traverse_recurse(resources, queue, ItemStack(v)) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- checks if the item/group is in the resource list and subtracts it
|
|
-- is the base case for traverse_recurse
|
|
local function has_sub(resources, item)
|
|
for i, v in ipairs(get_items_of(item:get_name())) do
|
|
if resources[v] and resources[v] >= item:get_count() then
|
|
resources[v] = resources[v] - item:get_count()
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- enqueues a recipe for an item and its needed sub items
|
|
function autocraft.traverse_recurse(resources, queue, item)
|
|
if type(item) == "string" then
|
|
item = ItemStack(item)
|
|
end
|
|
|
|
-- base case, uncraftibles/already in inventory
|
|
if has_sub(resources, item) then
|
|
return true
|
|
else
|
|
for i, v in ipairs(get_recipes(item:get_name())) do
|
|
if can_craft(resources, v.recipe) then
|
|
if recurse_recipe(resources, queue, v.recipe) then
|
|
table.insert(queue, v.recipe)
|
|
|
|
local tgt = item:get_count()
|
|
local result = v.count or 1
|
|
local delta = tgt - result
|
|
|
|
resources[v.name] = (resources[v.name] or 0) + result
|
|
|
|
if delta > 0 then
|
|
item:set_count(delta)
|
|
traverse_recurse(resources, queue, item)
|
|
end
|
|
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- not enough resources
|
|
return false
|
|
end
|
|
|
|
-- create a crafting queue for an item
|
|
-- return queue if enough resources, nil if not
|
|
function autocraft.traverse(item)
|
|
local queue = {}
|
|
local resources = count_inv()
|
|
|
|
if autocraft.traverse_recurse(resources, queue, item) then
|
|
return queue
|
|
end
|
|
end
|
|
|
|
-- craft a traversed craft tree
|
|
local function queuecraft(tree)
|
|
|
|
end
|
|
|
|
-- make a queue and craft it
|
|
function autocraft.craft(item)
|
|
|
|
end
|