local chest = minetest.get_content_id("mcl_chests:chest") local mcl_hoppers_formspec = "size[9,7]".. "background[-0.19,-0.25;9.41,10.48;mcl_hoppers_inventory.png]".. mcl_vars.inventory_header.. "list[current_name;main;2,0.5;5,1;]".. "list[current_player;main;0,2.5;9,3;9]".. "list[current_player;main;0,5.74;9,1;]".. "listring[current_name;main]".. "listring[current_player;main]" minetest.register_node("mcl_hoppers:hopper", { description = "Hopper", inventory_image = "mcl_hoppers_item.png", _doc_items_longdesc = [[Hoppers are containers with 5 inventory slots. They collect dropped items from above, take items from a container above and attempts to put its items it into an adjacent container. Hoppers can go either downwards or sideways. Hoppers interact with chests, droppers, dispensers, shulker boxes, furnaces and hoppers. Hoppers interact with containers the following way: • Furnaces: Hoppers from above will put items into the source slot. Hoppers from below take items from the output slot. They also take items from the fuel slot when they can't be used as a fuel. Sideway hoppers put items into the fuel slot • Ender chests: Hoppers don't interact with ender chests • Other containers: Hoppers interact with them normally]], _doc_items_usagehelp = "To place a hopper vertically, place it on the floor or a ceiling. To place it sideways, place it at the side of a block. Remember you can place at usable blocks (such as chests) with sneak + right-click. The hopper will keep its orientation when the blocks around it are changed. To access the hopper's inventory, rightclick it.", wield_image = "mcl_hoppers_item.png", groups = {pickaxey=1, container=2,deco_block=1,}, drawtype = "nodebox", paramtype = "light", tiles = {"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_inside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"}, selection_box = {type="regular"}, node_box = { type = "fixed", fixed = { --funnel walls {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5}, {0.4, 0.0, -0.5, 0.5, 0.5, 0.5}, {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5}, {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4}, --funnel base {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5}, --spout {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, {-0.15, -0.3, -0.15, 0.15, -0.5, 0.15}, }, }, is_ground_content = false, on_construct = function(pos) local meta = minetest.get_meta(pos) meta:set_string("formspec", mcl_hoppers_formspec) local inv = meta:get_inventory() inv:set_size("main", 5) end, on_place = function(itemstack, placer, pointed_thing) local upos = pointed_thing.under local apos = pointed_thing.above local uposnode = minetest.get_node(upos) local uposnodedef = minetest.registered_nodes[uposnode.name] -- Use pointed node's on_rightclick function first, if present if placer and not placer:get_player_control().sneak then if uposnodedef and uposnodedef.on_rightclick then return uposnodedef.on_rightclick(pointed_thing.under, uposnode, placer, itemstack) or itemstack end end local bpos if uposnodedef.buildable_to then bpos = upos else local aposnodedef = minetest.registered_nodes[minetest.get_node(apos).name] if aposnodedef.buildable_to then bpos = apos end end if bpos == nil then return itemstack end local x = upos.x - apos.x local y = upos.y - apos.y local z = upos.z - apos.z if x == -1 then minetest.set_node(bpos, {name="mcl_hoppers:hopper_side", param2=0}) elseif x == 1 then minetest.set_node(bpos, {name="mcl_hoppers:hopper_side", param2=2}) elseif z == -1 then minetest.set_node(bpos, {name="mcl_hoppers:hopper_side", param2=3}) elseif z == 1 then minetest.set_node(bpos, {name="mcl_hoppers:hopper_side", param2=1}) else minetest.set_node(bpos, {name="mcl_hoppers:hopper", param2=0}) end if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end return itemstack end, after_dig_node = function(pos, oldnode, oldmetadata, digger) local meta = minetest.get_meta(pos) local meta2 = meta meta:from_table(oldmetadata) local inv = meta:get_inventory() for i=1,inv:get_size("main") do local stack = inv:get_stack("main", i) if not stack:is_empty() then local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5} minetest.add_item(p, stack) end end meta:from_table(meta2:to_table()) end, on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) minetest.log("action", player:get_player_name().. " moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_put = function(pos, listname, index, stack, player) minetest.log("action", player:get_player_name().. " moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_take = function(pos, listname, index, stack, player) minetest.log("action", player:get_player_name().. " takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos)) end, sounds = mcl_sounds.node_sound_metal_defaults(), _mcl_blast_resistance = 24, _mcl_hardness = 3, }) minetest.register_node("mcl_hoppers:hopper_side", { description = "Hopper (Side)", _doc_items_create_entry = false, drop = "mcl_hoppers:hopper", groups = {pickaxey=1, container=2,not_in_creative_inventory=1}, drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", tiles = {"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_inside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"}, selection_box = {type="regular"}, node_box = { type = "fixed", fixed = { --funnel walls {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5}, {0.4, 0.0, -0.5, 0.5, 0.5, 0.5}, {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5}, {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4}, --funnel base {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5}, --spout {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, {-0.7, -0.3, -0.15, 0.15, 0.0, 0.15}, }, }, is_ground_content = false, on_construct = function(pos) local meta = minetest.get_meta(pos) meta:set_string("formspec", mcl_hoppers_formspec) local inv = meta:get_inventory() inv:set_size("main", 5) end, after_dig_node = function(pos, oldnode, oldmetadata, digger) local meta = minetest.get_meta(pos) local meta2 = meta meta:from_table(oldmetadata) local inv = meta:get_inventory() for i=1,inv:get_size("main") do local stack = inv:get_stack("main", i) if not stack:is_empty() then local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5} minetest.add_item(p, stack) end end meta:from_table(meta2:to_table()) end, on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) minetest.log("action", player:get_player_name().. " moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_put = function(pos, listname, index, stack, player) minetest.log("action", player:get_player_name().. " moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_take = function(pos, listname, index, stack, player) minetest.log("action", player:get_player_name().. " takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos)) end, sounds = mcl_sounds.node_sound_metal_defaults(), _mcl_blast_resistance = 24, _mcl_hardness = 3, }) -- Make hoppers suck in dropped items minetest.register_abm({ nodenames = {"mcl_hoppers:hopper","mcl_hoppers:hopper_side"}, interval = 1.0, chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) local abovenode = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) -- Don't bother checking item enties if node above is a container (should save some CPU) if minetest.registered_items[abovenode.name].groups.container then return end local meta = minetest.get_meta(pos) local inv = meta:get_inventory() for _,object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then if inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then local posob = object:getpos() if math.abs(posob.x-pos.x) <= 0.5 and (posob.y-pos.y <= 0.85 and posob.y-pos.y >= 0.3) then inv:add_item("main", ItemStack(object:get_luaentity().itemstring)) object:get_luaentity().itemstring = "" object:remove() end end end end end, }) -- Iterates through all items in the given inventory and -- return the slot of the first item which matches a condition local get_eligible_transfer_item = function(inventory, list, condition) local size = inventory:get_size(list) local stack for i=1, size do stack = inventory:get_stack(list, i) if not stack:is_empty() and condition(stack) then return i end end return nil end -- Returns true if given itemstack is a shulker box local is_not_shulker_box = function(itemstack) local g = minetest.get_item_group(itemstack:get_name(), "shulker_box") return g == 0 or g == nil end minetest.register_abm({ nodenames = {"mcl_hoppers:hopper"}, neighbors = {"group:container"}, interval = 1.0, chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) -- Get node pos' for item transfer local uppos = {x=pos.x,y=pos.y+1,z=pos.z} local downpos = {x=pos.x,y=pos.y-1,z=pos.z} -- Suck an item from the container above into the hopper local upnode = minetest.get_node(uppos) local g = minetest.registered_nodes[upnode.name].groups.container if g == 2 or g == 3 then -- Typical container inventory mcl_util.move_item_container(uppos, "main", -1, pos) elseif g == 4 then -- Furnace output mcl_util.move_item_container(uppos, "dst", -1, pos) -- Also suck in non-fuel items from fuel slot local finv = minetest.get_inventory({type="node", pos=uppos}) if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then mcl_util.move_item_container(uppos, "fuel", -1, pos) end end -- Move an item from the hopper into container below local downnode = minetest.get_node(downpos) g = minetest.registered_nodes[downnode.name].groups.container local slot_id = -1 if g == 3 then -- For shulker boxes, only select non-shulker boxes local sinv = minetest.get_inventory({type="node", pos = pos}) slot_id = get_eligible_transfer_item(sinv, "main", is_not_shulker_box) end if slot_id then mcl_util.move_item_container(pos, "main", slot_id, downpos) end end, }) minetest.register_abm({ nodenames = {"mcl_hoppers:hopper_side"}, neighbors = {"group:container"}, interval = 1.0, chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) -- Determine to which side the hopper is facing, get nodes local face = minetest.get_node(pos).param2 local front = {} if face == 0 then front = {x=pos.x-1,y=pos.y,z=pos.z} elseif face == 1 then front = {x=pos.x,y=pos.y,z=pos.z+1} elseif face == 2 then front = {x=pos.x+1,y=pos.y,z=pos.z} elseif face == 3 then front = {x=pos.x,y=pos.y,z=pos.z-1} end local above = {x=pos.x,y=pos.y+1,z=pos.z} local frontnode = minetest.get_node(front) -- Suck an item from the container above into the hopper local abovenode = minetest.get_node(above) local g = minetest.registered_nodes[abovenode.name].groups.container if g == 2 or g == 3 then -- Typical container inventory mcl_util.move_item_container(above, "main", -1, pos) elseif g == 4 then -- Furnace output mcl_util.move_item_container(above, "dst", -1, pos) end -- Move an item from the hopper into the container to which the hopper points to local g = minetest.registered_nodes[frontnode.name].groups.container if g == 2 then mcl_util.move_item_container(pos, "main", -1, front) elseif g == 3 then -- Put non-shulker boxes into shulker box local sinv = minetest.get_inventory({type="node", pos = pos}) local slot_id = get_eligible_transfer_item(sinv, "main", is_not_shulker_box) if slot_id then mcl_util.move_item_container(pos, "main", slot_id, front) end elseif g == 4 then -- Put fuel into fuel slot local sinv = minetest.get_inventory({type="node", pos = pos}) local slot_id = get_eligible_transfer_item(sinv, "main", mcl_util.is_fuel) if slot_id then mcl_util.move_item_container(pos, "main", slot_id, front, "fuel") end end end }) minetest.register_craft({ output = "mcl_hoppers:hopper", recipe = { {"mcl_core:iron_ingot","","mcl_core:iron_ingot"}, {"mcl_core:iron_ingot","mcl_chests:chest","mcl_core:iron_ingot"}, {"","mcl_core:iron_ingot",""}, } }) -- Legacy minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")