local utils = ... local S = utils.S local crafter_interval = 1.0 local function unit_inventory (pos, owner, inv_list) local node = utils.get_far_node (pos) if node and (node.name == "lwcomponents:storage_unit" or node.name == "lwcomponents:storage_unit_locked") then local meta = minetest.get_meta (pos) local uowner = meta:get_string ("owner") if meta and (owner == uowner or uowner == "") then local inv = meta:get_inventory () if inv then local slots = inv:get_size ("main") for slot = 1, slots, 1 do local stack = inv:get_stack ("main", slot) if stack and not stack:is_empty () then local copy = ItemStack (stack) copy:set_count (1) local name = copy:get_name () local item = inv_list[name] if not item then inv_list[name] = { name = name, count = 0 } item = inv_list[name] end item[#item + 1] = { pos = vector.new (pos), count = stack:get_count (), slot = slot } item.count = item.count + stack:get_count () end end end return true end end return false end local function inventory_searcher (pos, owner, coords, inv_list, check_list) local spos = minetest.pos_to_string (pos, 0) if not check_list[spos] then check_list[spos] = true if unit_inventory (pos, owner, inv_list) then for _, c in ipairs (coords) do inventory_searcher (vector.add (pos, c), owner, coords, inv_list, check_list) end end end end local function get_inventory_list (pos) local inv_list = { } local meta = minetest.get_meta (pos) if meta then local owner = meta:get_string ("owner") local check_list = { [minetest.pos_to_string (pos, 0)] = true } local coords = { { x = 1, y = 0, z = 0 }, { x = -1, y = 0, z = 0 }, { x = 0, y = 1, z = 0 }, { x = 0, y = -1, z = 0 }, { x = 0, y = 0, z = 1 }, { x = 0, y = 0, z = -1 } } for _, c in ipairs (coords) do inventory_searcher (vector.add (pos, c), owner, coords, inv_list, check_list) end local inv = meta:get_inventory () if inv then local slots = inv:get_size ("main") for slot = 1, slots, 1 do local stack = inv:get_stack ("main", slot) if stack and not stack:is_empty () then local copy = ItemStack (stack) copy:set_count (1) local name = copy:get_name () local item = inv_list[name] if not item then inv_list[name] = { name = name, count = 0 } item = inv_list[name] end item[#item + 1] = { pos = vector.new (pos), count = stack:get_count (), slot = slot } item.count = item.count + stack:get_count () end end end end return inv_list end local function get_stock_list (pos) local inv_list = get_inventory_list (pos) local list = { } for k, v in pairs (inv_list) do local description = "" local def = minetest.registered_items[k] if def and (def.short_description or def.description) then description = def.short_description or def.description end list[#list + 1] = { name = k, description = utils.unescape_description (description), count = v.count, } end return list end local function get_source_item (item, inv_list, used) if not item or type (item) ~= "string" or item:len () < 1 then return "" end if item:sub (1, 6) ~= "group:" then local name = inv_list[item] if name and (name.count - (used[name] or 0)) > 0 then if used[name] then used[name] = used[name] + 1 else used[name] = 1 end return name.name end return nil end local group = item:sub (7) for k, v in pairs (inv_list) do if minetest.get_item_group (k, group) > 0 then if (v.count - (used[k] or 0)) > 0 then if used[k] then used[k] = used[k] + 1 else used[k] = 1 end return k end end end return nil end local function get_craft_recipe_items (items, inv_list) local recipe = { } local valid = false local used = { } for i = 1, 9, 1 do if items[i] then recipe[i] = get_source_item (items[i], inv_list, used) if not recipe[i] then return nil end valid = true end end if not valid then return nil end return recipe end local function get_craft_items (itemname, inv_list) local recipes = minetest.get_all_craft_recipes (itemname) if recipes then for i, recipe in ipairs (recipes) do if (recipe.type and recipe.type == "normal") or (recipe.method and recipe.method == "normal") then local items = get_craft_recipe_items (recipe.items, inv_list) if items then return items, recipe end end end end return nil end local function get_craftable_list (pos) local inv_list = get_inventory_list (pos) local list = { } for itemname, def in pairs (minetest.registered_items) do if (get_craft_items (itemname, inv_list)) then local description = def.short_description or def.description list[#list + 1] = { name = itemname, description = utils.unescape_description (description or "") } end end return list end local function remove_input_items (item) local meta = minetest.get_meta (item.pos) if meta then local inv = meta:get_inventory () if inv then local stack = inv:get_stack ("main", item.slot) if stack and not stack:is_empty () and stack:get_count () >= item.count and stack:get_name () == item.name then if stack:get_count () > item.count then stack:set_count (stack:get_count () - item.count) inv:set_stack ("main", item.slot, stack) else inv:set_stack ("main", item.slot, nil) end return true end end end return false end local function return_input_items (item) local meta = minetest.get_meta (item.pos) if meta then local inv = meta:get_inventory () if inv then local stack = inv:get_stack ("main", item.slot) if not stack or stack:is_empty () then inv:set_stack ("main", item.slot, ItemStack (string.format ("%s %d", item.name, item.count))) return true elseif stack:get_name () == item.name then local count = stack:get_count () + item.count if stack:get_stack_max () < count then count = stack:get_stack_max () - stack:get_count () end if count > 0 then stack:set_count (count) inv:set_stack ("main", item.slot, stack) end return true end end end return false end -- removes items from storage and returns list where they were taken from or nil local function get_input_items (items, inv_list) local agg = { } local input = { } for i = 1, 9, 1 do if items[i] then if items[i]:len () > 0 then if agg[items[i]] then agg[items[i]] = agg[items[i]] + 1 else agg[items[i]] = 1 end end end end for k, c in pairs (agg) do local list = inv_list[k] if not list then return nil end if list.count < c then return nil end local count = c for i = #list, 1, -1 do if list[i].count <= count then input[#input + 1] = { name = k, pos = list[i].pos, slot = list[i].slot, count = list[i].count } count = count - list[i].count list[i] = nil else input[#input + 1] = { name = k, pos = list[i].pos, slot = list[i].slot, count = count } list[i].count = list[i].count - count count = 0 end if count < 1 then break end end end for i = 1, #input, 1 do if not remove_input_items (input[i]) then -- put back for j = i - 1, 1, -1 do return_input_items (input[j]) end return nil end end return input end -- return inv table after add or nil local function can_fit_output (pos, output) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then local copy = { } local list = { } local slots = inv:get_size ("output") for i = 1, #output, 1 do copy[i] = ItemStack (output[i]) end for i = 1, slots, 1 do list[i] = inv:get_stack ("output", i) end for i = 1, #copy, 1 do if copy[i]:get_count () > 0 then for j = 1, #list, 1 do if utils.is_same_item (list[j], copy[i]) then if (list[j]:get_count () + copy[i]:get_count ()) > list[j]:get_stack_max () then copy[i]:set_count (list[j]:get_stack_max () - list[j]:get_count ()) list[j]:set_count (list[j]:get_stack_max ()) else list[j]:set_count (list[j]:get_count () + copy[i]:get_count ()) copy[i]:set_count (0) end end if copy[i]:get_count () < 1 then break end end end end for i = 1, #copy, 1 do if copy[i]:get_count () > 0 then for j = 1, #list, 1 do if list[j]:is_empty () then list[j] = ItemStack (copy[i]) copy[i]:set_count (0) end if copy[i]:get_count () < 1 then break end end end end for i = 1, #copy, 1 do if copy[i]:get_count () > 0 then return nil end end return list end end return nil end local function update_output (pos, list) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then inv:set_list ("output", list) return true end end return false end local function place_in_output (pos, stack) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then inv:add_item ("output", stack) end end end -- items is list of recipe grid local function craft (pos, items, recipe, qty, inv_list) local output, leftover = minetest.get_craft_result (recipe) local crafted = 0 if output and output.item and not output.item:is_empty () then for q = 1, qty, 1 do -- check for output space local output_items = { ItemStack (output.item) } for i = 1, #output.replacements, 1 do if output.replacements[i] and not output.replacements[i]:is_empty () then output_items[#output_items + 1] = ItemStack (output.replacements[i]) end end -- implement crafting_mods.lua local mods = utils.get_crafting_mods (output.item:get_name ()) local remove_items = { } if mods then if mods.add then for i = 1, #mods.add do output_items[#output_items + 1] = ItemStack (mods.add[i]) end end if mods.remove then for i = 1, #mods.remove do local found = false for j = #output_items, 1, -1 do if output_items[j]:get_name () == mods.remove[i] then if output_items[j]:get_count () > 1 then output_items[j]:set_count (output_items[j]:get_count () - 1) else output_items[j] = nil end found = true end end if not found then remove_items[#remove_items + 1] = mods.remove[i] end end end end -- get updated output inv if can fit local output_list = can_fit_output (pos, output_items) if output_list then local input_items = get_input_items (items, inv_list) if not input_items then return crafted end if not update_output (pos, output_list) then for j = 1, #input_items, 1 do return_input_items (input_items[j]) end return crafted end for i = 1, #leftover.items, 1 do if leftover.items[i] and not leftover.items[i]:is_empty () then local count = leftover.items[i]:get_count () for j = 1, #input_items, 1 do if input_items[j].name == leftover.items[i]:get_name () then local over = { name = input_items[j].name, pos = input_items[j].pos, slot = input_items[j].slot, count = input_items[j].count } if over.count < count then count = count - over.count else over.count = count count = 0 end if not return_input_items (over) then place_in_output (pos, ItemStack (string.format ("%s %d", over.name, count))) end if count < 1 then break end end end if count > 0 then place_in_output (pos, ItemStack (string.format ("%s %d", leftover.items[i]:get_name (), count))) end end end -- removes from crafting_mods.lua if not taken from replacements if #remove_items > 0 then get_input_items (remove_items, inv_list) end crafted = crafted + 1 end end end return crafted end local function get_recipe_grid (pos) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then local grid = { } local slots = inv:get_size ("craft") for i = 1, slots, 1 do local stack = inv:get_stack ("craft", i) if stack then grid[i] = stack:get_name () else grid[i] = "" end end return grid end end return nil end local function set_recipe_grid (pos, items) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then if type (items) ~= "table" then items = { } end for i = 1, 9, 1 do if items[i] and minetest.registered_items[items[i]] then inv:set_stack ("craft", i, ItemStack (items[i])) else inv:set_stack ("craft", i, nil) end end end end end local function craft_recipe (pos, qty) local inv_list = get_inventory_list (pos) local items = get_recipe_grid (pos) local recipe = { method = "normal", width = 3, items = get_recipe_grid (pos) } return craft (pos, items, recipe, qty, inv_list) end local function craft_item (pos, itemname, qty) local inv_list = get_inventory_list (pos) local items, recipe = get_craft_items (itemname, inv_list) if items and recipe then return craft (pos, items, recipe, qty, inv_list) end return 0 end local function preview_craft (pos) local items = get_recipe_grid (pos) local recipe = { method = "normal", width = 3, items = table.copy (items) } local item local output = minetest.get_craft_result (recipe) if output and output.item and not output.item:is_empty () then item = output.item end local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then inv:set_stack ("preview", 1, item) end end end local function run_automatic (pos, run) local timer = minetest.get_node_timer (pos) local meta = minetest.get_meta (pos) if timer and meta then if run then if not timer:is_started () then timer:start (crafter_interval) meta:set_string ("automatic", "true") end elseif timer:is_started () then timer:stop () meta:set_string ("automatic", "false") end end end local function search_filter (name, terms) if terms then for _, t in ipairs (terms) do if (name:lower ():find (t, 1, true)) then return true end end return false end return true end local function filter_craftable_list (list, search) if search and tostring (search):len () > 1 then local terms = { } for w in string.gmatch(search, "[^%s]+") do terms[#terms + 1] = string.lower (w) end if #terms > 0 then local filtered = { } for i = 1, #list, 1 do if search_filter (list[i].description or "", terms) or search_filter (list[i].name or "", terms) then filtered[#filtered + 1] = list[i] end end return filtered end end return list end local function get_formspec (pos, search) local spec = "" local meta = minetest.get_meta (pos) if meta then local list = filter_craftable_list (get_craftable_list (pos), search) local crafts = "" local automatic = (meta:get_string ("automatic") == "true" and "true") or "false" local lines = math.ceil (#list / 5) local scroll_height = ((lines < 11 and 0) or (lines - 10)) * 10 local thumb_size = (lines < 11 and 10) or (scroll_height * (10 / lines)) if thumb_size < (scroll_height / 9) then thumb_size = scroll_height / 9 end search = search or "" for i, item in pairs (list) do crafts = crafts..string.format ("item_image_button[%d,%d;1.0,1.0;%s;ITEM_%s;]", ((i - 1) % 5), math.floor ((i - 1) / 5), item.name, item.name) end spec = string.format ("formspec_version[3]".. "size[24.7,13.0,false]".. "set_focus[search_field;true]".. "label[1.0,1.0;Input]".. "list[context;main;1.0,1.25;8,4;]".. "listring[current_player;main]".. "list[current_player;main;1.0,7.2;8,4;]".. "listring[context;main]".. "field[11.5,1.21;3.0,0.8;channel;Channel;${channel}]".. "field_close_on_enter[channel;false]".. "button[14.5,1.21;1.5,0.8;setchannel;Set]".. "list[context;craft;11.5,2.5;3,3;]".. "checkbox[15.5,2.7;automatic;Automatic;%s]".. "button[15.5,5.3;2.0,0.8;craft;Craft]".. "list[context;preview;16.0,3.75;1,1;]".. "label[11.5,6.95;Output]".. "list[context;output;11.5,7.2;5,4;]".. "listring[current_player;main]".. "field[16.5,1.21;5.2,0.8;search_field;;%s]".. "field_close_on_enter[search_field;false]".. "button[21.7,1.21;2.0,0.8;search;Search]".. "scrollbaroptions[min=0;max=%d;smallstep=10;largestep=100;thumbsize=%d;arrows=default]".. "scrollbar[23.2,2.0;0.5,10.0;vertical;crafter_scrollbar;0-%d]".. "scroll_container[18.2,2.0;5.0,10.0;crafter_scrollbar;vertical;0.1]".. "%s".. "scroll_container_end[]", automatic, search, scroll_height, thumb_size, scroll_height, crafts) end return spec end local function after_place_node (pos, placer, itemstack, pointed_thing) local meta = minetest.get_meta (pos) meta:set_string ("inventory", "{ main = { }, output = { }, craft = { }, preview = { } }") meta:set_string ("automatic", "false") local inv = meta:get_inventory () inv:set_size ("main", 32) inv:set_width ("main", 8) inv:set_size ("output", 20) inv:set_width ("output", 5) inv:set_size ("craft", 9) inv:set_width ("craft", 3) inv:set_size ("preview", 1) inv:set_width ("preview", 1) meta:set_string ("formspec", get_formspec (pos)) -- If return true no item is taken from itemstack return false end local function after_place_node_locked (pos, placer, itemstack, pointed_thing) after_place_node (pos, placer, itemstack, pointed_thing) if placer and placer:is_player () then local meta = minetest.get_meta (pos) meta:set_string ("owner", placer:get_player_name ()) meta:set_string ("infotext", "Crafter (owned by "..placer:get_player_name ()..")") end -- If return true no item is taken from itemstack return false end local function can_dig (pos, player) if not utils.can_interact_with_node (pos, player) then return false end local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then if not inv:is_empty ("main") or not inv:is_empty ("output") then return false end end end return true end local function on_blast (pos, intensity) local meta = minetest.get_meta (pos) if meta then if intensity >= 1.0 then local inv = meta:get_inventory () if inv then local slots = inv:get_size ("main") for slot = 1, slots do local stack = inv:get_stack ("main", slot) if stack and not stack:is_empty () then if math.floor (math.random (0, 5)) == 3 then utils.item_drop (stack, nil, pos) else utils.on_destroy (stack) end end end slots = inv:get_size ("output") for slot = 1, slots do local stack = inv:get_stack ("output", slot) if stack and not stack:is_empty () then if math.floor (math.random (0, 5)) == 3 then utils.item_drop (stack, nil, pos) else utils.on_destroy (stack) end end end end minetest.remove_node (pos) else -- intensity < 1.0 local inv = meta:get_inventory () if inv then local slots = inv:get_size ("main") for slot = 1, slots do local stack = inv:get_stack ("main", slot) if stack and not stack:is_empty () then utils.item_drop (stack, nil, pos) end end slots = inv:get_size ("output") for slot = 1, slots do local stack = inv:get_stack ("output", slot) if stack and not stack:is_empty () then utils.item_drop (stack, nil, pos) end end end local node = minetest.get_node_or_nil (pos) if node then local items = minetest.get_node_drops (node, nil) if items and #items > 0 then local stack = ItemStack (items[1]) if stack then utils.item_drop (stack, nil, pos) minetest.remove_node (pos) end end end end end end local function on_rightclick (pos, node, clicker, itemstack, pointed_thing) if not utils.can_interact_with_node (pos, clicker) then if clicker and clicker:is_player () then local owner = "" local meta = minetest.get_meta (pos) if meta then owner = meta:get_string ("owner") end local spec = "formspec_version[3]".. "size[8.0,4.0,false]".. "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. "button_exit[3.0,2.0;2.0,1.0;close;Close]" minetest.show_formspec (clicker:get_player_name (), "lwcomponents:component_privately_owned", spec) end else local meta = minetest.get_meta (pos) if meta then meta:set_string ("formspec", get_formspec (pos)) end end return itemstack end local function on_receive_fields (pos, formname, fields, sender) if not utils.can_interact_with_node (pos, sender) then return end if fields.setchannel or (fields.key_enter_field and fields.key_enter_field == "channel") then local meta = minetest.get_meta (pos) if meta then meta:set_string ("channel", fields.channel) end elseif fields.automatic then local meta = minetest.get_meta (pos) if meta then meta:set_string ("automatic", fields.automatic) run_automatic (pos, fields.automatic == "true") end elseif fields.search or (fields.key_enter_field and fields.key_enter_field == "search_field") then local meta = minetest.get_meta (pos) if meta then meta:set_string ("formspec", get_formspec (pos, fields.search_field)) end elseif fields.craft then craft_recipe (pos, 1) else for k, v in pairs (fields) do if k:sub (1, 5) == "ITEM_" then local itemname = k:sub (6, -1) craft_item (pos, itemname, 1) break end end end end local function allow_metadata_inventory_take (pos, listname, index, stack, player) if listname == "preview" then return 0 end if listname == "craft" then local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then inv:set_stack ("craft", index, nil) preview_craft (pos) end end return 0 end return stack:get_stack_max () end local function allow_metadata_inventory_put (pos, listname, index, stack, player) if listname == "preview" then return 0 end if listname == "craft" then local copy = ItemStack (stack) if copy and not copy:is_empty () then copy:set_count (1) local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then inv:set_stack ("craft", index, copy) preview_craft (pos) end end end return 0 end return stack:get_stack_max () end local function allow_metadata_inventory_move (pos, from_list, from_index, to_list, to_index, count, player) if from_list == "preview" or to_list == "preview" then return 0 end local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then if from_list == "craft" then if to_list == "craft" then return 1 end inv:set_stack ("craft", from_index, nil) preview_craft (pos) return 0 elseif to_list == "craft" then local stack = inv:get_stack (from_list, from_index) if stack and not stack:is_empty () then inv:set_stack ("craft", to_index, ItemStack (stack:get_name ())) preview_craft (pos) end return 0 else local stack = inv:get_stack (from_list, from_index) if stack and not stack:is_empty () then return stack:get_stack_max () end end end end return utils.settings.default_stack_max end local function on_metadata_inventory_take (pos, listname, index, stack, player) if listname == "craft" then preview_craft (pos) end end local function on_metadata_inventory_put (pos, listname, index, stack, player) if listname == "craft" then preview_craft (pos) end end local function on_metadata_inventory_move (pos, from_list, from_index, to_list, to_index, count, player) if from_list == "preview" or to_list == "preview" then return end if from_list == "craft" or to_list == "craft" then preview_craft (pos) end local meta = minetest.get_meta (pos) if meta then local inv = meta:get_inventory () if inv then if from_list == "craft" then if to_list ~= "craft" then inv:set_stack ("craft", from_index, nil) preview_craft (pos) end elseif to_list == "craft" then local stack = inv:get_stack (from_list, from_index) if stack and not stack:is_empty () then inv:set_stack ("craft", to_index, ItemStack (stack:get_name ())) preview_craft (pos) end end end end end local function on_timer (pos, elapsed) craft_recipe (pos, 1) return true end local function mesecon_support () if utils.mesecon_supported then return { effector = { rules = utils.mesecon_flat_rules, action_on = function (pos, node) craft_recipe (pos, 1) end } } end return nil end local function send_stock_message (pos) if utils.digilines_supported then local meta = minetest.get_meta (pos) if meta then local channel = meta:get_string ("channel") if channel:len () > 0 then local msg = { action = "inventory", inventory = get_stock_list (pos) } utils.digilines_receptor_send (pos, utils.digilines_default_rules, channel, msg) end end end end local function send_craftable_message (pos) if utils.digilines_supported then local meta = minetest.get_meta (pos) if meta then local channel = meta:get_string ("channel") if channel:len () > 0 then local msg = { action = "craftable", items = get_craftable_list (pos) } utils.digilines_receptor_send (pos, utils.digilines_default_rules, channel, msg) end end end end local function digilines_support () if utils.digilines_supported then return { wire = { rules = utils.digilines_default_rules, }, effector = { action = function (pos, node, channel, msg) local meta = minetest.get_meta(pos) if meta then local this_channel = meta:get_string ("channel") if this_channel ~= "" then if type (msg) == "string" then local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w end if this_channel == channel then if m[1] == "craft" then local qty = 1 if m[2] and tonumber (m[2]) then qty = math.floor (math.max (math.min (tonumber (m[2]), 10), 1)) end craft_recipe (pos, qty) elseif m[1] == "craftitem" then if m[2] and minetest.registered_items[m[2]] then local qty = 1 if m[3] and tonumber (m[3]) then qty = math.floor (math.max (math.min (tonumber (m[3]), 10), 1)) end craft_item (pos, m[2], qty) end elseif m[1] == "automatic" then run_automatic (pos, tostring (m[2]) == "true") elseif m[1] == "craftable" then send_craftable_message (pos) elseif m[1] == "inventory" then send_stock_message (pos) end end elseif type (msg) == "table" then if this_channel == channel then if msg.action and tostring (msg.action) == "recipe" then set_recipe_grid (pos, msg.items) end end end end end end, } } end return nil end local function pipeworks_support () if utils.pipeworks_supported then return { priority = 100, input_inventory = "output", connect_sides = { left = 1, right = 1, front = 1, back = 1, bottom = 1, top = 1 }, insert_object = function (pos, node, stack, direction) local meta = minetest.get_meta (pos) local inv = (meta and meta:get_inventory ()) or nil if inv then return inv:add_item ("main", stack) end return stack end, can_insert = function (pos, node, stack, direction) local meta = minetest.get_meta (pos) local inv = (meta and meta:get_inventory ()) or nil if inv then return inv:room_for_item ("main", stack) end return false end, can_remove = function (pos, node, stack, dir) -- returns the maximum number of items of that stack that can be removed local meta = minetest.get_meta (pos) local inv = (meta and meta:get_inventory ()) or nil if inv then local slots = inv:get_size ("output") for i = 1, slots, 1 do local s = inv:get_stack ("output", i) if s and not s:is_empty () and utils.is_same_item (stack, s) then return s:get_count () end end end return 0 end, remove_items = function (pos, node, stack, dir, count) -- removes count items and returns them local meta = minetest.get_meta (pos) local inv = (meta and meta:get_inventory ()) or nil local left = count if inv then local slots = inv:get_size ("output") for i = 1, slots, 1 do local s = inv:get_stack ("output", i) if s and not s:is_empty () and utils.is_same_item (s, stack) then if s:get_count () > left then s:set_count (s:get_count () - left) inv:set_stack ("output", i, s) left = 0 else left = left - s:get_count () inv:set_stack ("output", i, nil) end end if left == 0 then break end end end local result = ItemStack (stack) result:set_count (count - left) return result end } end return nil end local crafter_groups = { choppy = 2 } if utils.pipeworks_supported then crafter_groups.tubedevice = 1 crafter_groups.tubedevice_receiver = 1 end minetest.register_node("lwcomponents:crafter", { description = S("Crafter"), drawtype = "normal", tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage_framed.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png" }, is_ground_content = false, groups = table.copy (crafter_groups), sounds = default.node_sound_wood_defaults (), paramtype = "none", param1 = 0, paramtype2 = "none", param2 = 0, floodable = false, _digistuff_channelcopier_fieldname = "channel", mesecons = mesecon_support (), digiline = digilines_support (), tube = pipeworks_support (), after_place_node = after_place_node, can_dig = can_dig, on_blast = on_blast, on_rightclick = on_rightclick, on_receive_fields = on_receive_fields, allow_metadata_inventory_take = allow_metadata_inventory_take, allow_metadata_inventory_put = allow_metadata_inventory_put, allow_metadata_inventory_move = allow_metadata_inventory_move, on_metadata_inventory_take = on_metadata_inventory_take, on_metadata_inventory_put = on_metadata_inventory_put, on_metadata_inventory_move = on_metadata_inventory_move, on_timer = on_timer }) minetest.register_node("lwcomponents:crafter_locked", { description = S("Crafter (locked)"), drawtype = "normal", tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage_framed.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png", "lwcomponents_storage_crafter.png" }, is_ground_content = false, groups = table.copy (crafter_groups), sounds = default.node_sound_wood_defaults (), paramtype = "none", param1 = 0, paramtype2 = "none", param2 = 0, floodable = false, _digistuff_channelcopier_fieldname = "channel", mesecons = mesecon_support (), digiline = digilines_support (), tube = pipeworks_support (), after_place_node = after_place_node_locked, can_dig = can_dig, on_blast = on_blast, on_rightclick = on_rightclick, on_receive_fields = on_receive_fields, allow_metadata_inventory_take = allow_metadata_inventory_take, allow_metadata_inventory_put = allow_metadata_inventory_put, allow_metadata_inventory_move = allow_metadata_inventory_move, on_metadata_inventory_take = on_metadata_inventory_take, on_metadata_inventory_put = on_metadata_inventory_put, on_metadata_inventory_move = on_metadata_inventory_move, on_timer = on_timer }) utils.hopper_add_container({ {"top", "lwcomponents:crafter", "output"}, -- take items from above into hopper below {"bottom", "lwcomponents:crafter", "main"}, -- insert items below from hopper above {"side", "lwcomponents:crafter", "main"}, -- insert items from hopper at side }) utils.hopper_add_container({ {"top", "lwcomponents:crafter_locked", "output"}, -- take items from above into hopper below {"bottom", "lwcomponents:crafter_locked", "main"}, -- insert items below from hopper above {"side", "lwcomponents:crafter_locked", "main"}, -- insert items from hopper at side }) --