395 lines
14 KiB
Lua
395 lines
14 KiB
Lua
|
|
|
|
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",
|
|
sunlight_propagates = true,
|
|
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.1, -0.3, -0.1, 0.1, -0.5, 0.1},
|
|
},
|
|
},
|
|
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]
|
|
if not uposnodedef then return itemstack end
|
|
-- 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 not aposnodedef then return itemstack end
|
|
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",
|
|
sunlight_propagates = true,
|
|
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.5, -0.3, -0.1, 0.1, -0.1, 0.1},
|
|
},
|
|
},
|
|
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({
|
|
label = "Hoppers suck in dropped items",
|
|
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})
|
|
if not minetest.registered_items[abovenode.name] then return end
|
|
-- 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, 2)) 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
|
|
-- Item must get sucked in when the item just TOUCHES the block above the hopper
|
|
-- This is the reason for the Y calculation.
|
|
-- Test: Items on farmland and slabs get sucked, but items on full blocks don't
|
|
local posob = object:getpos()
|
|
local posob_miny = posob.y + object:get_properties().collisionbox[2]
|
|
if math.abs(posob.x-pos.x) <= 0.5 and (posob_miny-pos.y < 1.5 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(src_inventory, src_list, dst_inventory, dst_list, condition)
|
|
local size = src_inventory:get_size(src_list)
|
|
local stack
|
|
for i=1, size do
|
|
stack = src_inventory:get_stack(src_list, i)
|
|
if not stack:is_empty() and condition(stack, src_inventory, src_list, dst_inventory, dst_list) 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
|
|
|
|
-- Returns true if itemstack is fuel, but not for lava bucket if destination already has one
|
|
local is_transferrable_fuel = function(itemstack, src_inventory, src_list, dst_inventory, dst_list)
|
|
if mcl_util.is_fuel(itemstack) then
|
|
if itemstack:get_name() == "mcl_buckets:bucket_lava" then
|
|
return dst_inventory:is_empty(dst_list)
|
|
else
|
|
return true
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
|
|
minetest.register_abm({
|
|
label = "Hopper/container item exchange",
|
|
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)
|
|
if not minetest.registered_nodes[upnode.name] then return end
|
|
local g = minetest.registered_nodes[upnode.name].groups.container
|
|
if g == 2 or g == 3 or g == 5 or g == 6 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)
|
|
if not minetest.registered_nodes[downnode.name] then return end
|
|
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})
|
|
local dinv = minetest.get_inventory({type="node", pos = downpos})
|
|
slot_id = get_eligible_transfer_item(sinv, "main", dinv, "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({
|
|
label = "Side-hopper/container item exchange",
|
|
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)
|
|
if not minetest.registered_nodes[frontnode.name] then return end
|
|
|
|
-- Suck an item from the container above into the hopper
|
|
local abovenode = minetest.get_node(above)
|
|
if not minetest.registered_nodes[abovenode.name] then return end
|
|
local g = minetest.registered_nodes[abovenode.name].groups.container
|
|
if g == 4 then
|
|
-- Furnace output
|
|
mcl_util.move_item_container(above, "dst", -1, pos)
|
|
else
|
|
-- Typical container inventory
|
|
mcl_util.move_item_container(above, "main", -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 or g == 5 or g == 6 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 dinv = minetest.get_inventory({type="node", pos = front})
|
|
local slot_id = get_eligible_transfer_item(sinv, "main", dinv, "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 dinv = minetest.get_inventory({type="node", pos = front})
|
|
local slot_id, stack = get_eligible_transfer_item(sinv, "main", dinv, "fuel", is_transferrable_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",""},
|
|
}
|
|
})
|
|
|
|
-- Add entry aliases for the Help
|
|
if minetest.get_modpath("doc") then
|
|
doc.add_entry_alias("nodes", "mcl_hoppers:hopper", "nodes", "mcl_hoppers:hopper_side")
|
|
end
|
|
|
|
-- Legacy
|
|
minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")
|