VoxeLibre/mods/ITEMS/mcl_hoppers/init.lua

394 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.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",
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.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({
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})
-- 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() == "bucket: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)
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})
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)
if g == 4 then
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
minetest.get_node_timer(downpos):start(1.0)
end
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)
-- 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 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
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
minetest.get_node_timer(front):start(1.0)
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")