diff options
| -rw-r--r-- | autocrafter.lua | 131 | 
1 files changed, 71 insertions, 60 deletions
| diff --git a/autocrafter.lua b/autocrafter.lua index 3579e8b..4216517 100644 --- a/autocrafter.lua +++ b/autocrafter.lua @@ -2,78 +2,85 @@ local autocrafterCache = {}  -- caches some recipe data to avoid to call the slo  local function count_index(invlist)  	local index = {} -	for _, stack in ipairs(invlist) do -		local stack_name = stack:get_name() -		index[stack_name] = (index[stack_name] or 0) + stack:get_count() +	for _, stack in pairs(invlist) do +		if not stack:is_empty() then +			local stack_name = stack:get_name() +			index[stack_name] = (index[stack_name] or 0) + stack:get_count() +		end  	end  	return index  end -local function get_cached_craft(pos) -	local hash = minetest.hash_node_position(pos) -	return hash, autocrafterCache[hash] +local function get_craft(pos, inventory, hash) +	local hash = hash or minetest.hash_node_position(pos) +	local craft = autocrafterCache[hash] +	if not craft then +		if inventory:is_empty("recipe") then return nil end +		local recipe = inventory:get_list("recipe") +		local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe}) +		craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input} +		autocrafterCache[hash] = craft + +	end +	-- only return crafts that have an actual result +	if not craft.output.item:is_empty() then +		return craft +	end  end  -- note, that this function assumes allready being updated to virtual items  -- and doesn't handle recipes with stacksizes > 1  local function on_recipe_change(pos, inventory) -	if not inventory then return end +	-- if we emptied the grid, there's no point in keeping it running or cached +	if inventory:is_empty("recipe") then +		minetest.get_node_timer(pos):stop() +		autocrafterCache[minetest.hash_node_position(pos)] = nil +		return +	end +	local recipe_changed = false  	local recipe = inventory:get_list("recipe") -	if not recipe then return end -	local recipe_changed = false -	local hash, craft = get_cached_craft(pos) +	local hash = minetest.hash_node_position(pos) +	local craft = autocrafterCache[hash] -	if not craft then -		recipe_changed = true -	else +	if craft then  		-- check if it changed -		local cached_recipe =  craft.recipe +		local cached_recipe = craft.recipe  		for i = 1, 9 do  			if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then -				recipe_changed = true +				autocrafterCache[hash] = nil -- invalidate recipe +				craft = nil  				break  			end  		end  	end -	if recipe_changed then -		local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe}) -		craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input} -		autocrafterCache[hash] = craft +	local timer = minetest.get_node_timer(pos) +	if not timer:is_started() then +		timer:start(1)  	end - -	return craft  end -local function update_autocrafter(pos) -	local meta = minetest.get_meta(pos) -	if meta:get_string("virtual_items") == "" then -		meta:set_string("virtual_items", "1") -		local inv = meta:get_inventory() -		for idx, stack in ipairs(inv:get_list("recipe")) do -			minetest.item_drop(stack, "", pos) -			stack:set_count(1) -			stack:set_wear(0) -			inv:set_stack("recipe", idx, stack) -		end -		on_recipe_change(pos, inv) +local function on_inventory_change(pos, inventory) +	local timer = minetest.get_node_timer(pos) +	if not timer:is_started() then +		timer:start(1)  	end  end  local function autocraft(inventory, craft) +	if not craft then return false end  	local output_item = craft.output.item +  	-- check if we have enough room in dst  	if not inventory:room_for_item("dst", output_item) then return false end -  	local consumption = craft.consumption  	local inv_index = count_index(inventory:get_list("src")) -	-- check if we have enough materials available +	-- check if we have enough material available  	for itemname, number in pairs(consumption) do  		if (not inv_index[itemname]) or inv_index[itemname] < number then return false end  	end - -	-- consume materials +	-- consume material  	for itemname, number in pairs(consumption) do  		for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max  			inventory:remove_item("src", ItemStack(itemname)) @@ -88,22 +95,28 @@ local function autocraft(inventory, craft)  	return true  end -local function run_autocrafter(inventory, pos) -	if not inventory then return end -	local recipe = inventory:get_list("recipe") -	if not recipe then return end +-- returns false to stop the timer, true to continue running +-- is started only from start_autocrafter(pos) after sanity checks and cached recipe +local function run_autocrafter(pos, elapsed) +	local meta = minetest.get_meta(pos) +	local inventory = meta:get_inventory() +	local craft = get_craft(pos, inventory) +	return autocraft(inventory, craft) +end -	local _, craft = get_cached_craft(pos) -	if craft == nil then -		update_autocrafter(pos) -- only does some unnecessary calls for "old" autocrafters -		craft = on_recipe_change(pos, inventory) +local function update_autocrafter(pos) +	local meta = minetest.get_meta(pos) +	if meta:get_string("virtual_items") == "" then +		meta:set_string("virtual_items", "1") +		local inv = meta:get_inventory() +		for idx, stack in ipairs(inv:get_list("recipe")) do +			minetest.item_drop(stack, "", pos) +			stack:set_count(1) +			stack:set_wear(0) +			inv:set_stack("recipe", idx, stack) +		end +		on_recipe_change(pos, inv)  	end - -	-- skip crafts that have no output -	local output_item = craft.output.item -	if output_item:is_empty() then return end - -	autocraft(inventory, craft)  end  minetest.register_node("pipeworks:autocrafter", { @@ -114,7 +127,9 @@ minetest.register_node("pipeworks:autocrafter", {  	tube = {insert_object = function(pos, node, stack, direction)  			local meta = minetest.get_meta(pos)  			local inv = meta:get_inventory() -			return inv:add_item("src", stack) +			local added = inv:add_item("src", stack) +			on_inventory_change(pos, inv) +			return added  		end,   		can_insert = function(pos, node, stack, direction)  			local meta = minetest.get_meta(pos) @@ -162,6 +177,7 @@ minetest.register_node("pipeworks:autocrafter", {  			on_recipe_change(pos, inv)  			return 0  		else +			on_inventory_change(pos, inv)  			return stack:get_count()  		end  	end, @@ -173,6 +189,7 @@ minetest.register_node("pipeworks:autocrafter", {  			on_recipe_change(pos, inv)  			return 0  		else +			on_inventory_change(pos, inv)  			return stack:get_count()  		end  	end, @@ -192,15 +209,9 @@ minetest.register_node("pipeworks:autocrafter", {  			on_recipe_change(pos, inv)  			return 0  		else +			on_inventory_change(pos, inv)  			return stack:get_count()  		end  	end, -}) - -minetest.register_abm({nodenames = {"pipeworks:autocrafter"}, interval = 1, chance = 1,  -			action = function(pos, node) -				local meta = minetest.get_meta(pos) -				local inv = meta:get_inventory() -				run_autocrafter(inv, pos) -			end +	on_timer = run_autocrafter  }) | 
