Merge pull request 'mcl_hoppers fixes' (#2983) from mcl-hoppers-fixes into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2983
Reviewed-by: ancientmarinerdev <ancientmariner_dev@proton.me>
ancientmarinerdev 2022-12-11 20:55:59 +00:00
commit 38e49a4b80
2 changed files with 226 additions and 181 deletions

@ -1,37 +1,48 @@
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_hoppers",false) local LOGGING_ON = minetest.settings:get_bool("mcl_logging_hoppers", false)
local function mcl_log (message) local function mcl_log(message)
if LOGGING_ON then if LOGGING_ON then
mcl_util.mcl_log (message, "[Hoppers]", true) mcl_util.mcl_log(message, "[Hoppers]", true)
end end
end end
--[[ BEGIN OF NODE DEFINITIONS ]] --[[ BEGIN OF NODE DEFINITIONS ]]
local mcl_hoppers_formspec = local mcl_hoppers_formspec = table.concat({
"size[9,7]".. "size[9,7]",
"label[2,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Hopper"))).."]".. "label[2,0;" .. F(C("#313131", S("Hopper"))) .. "]",
"list[context;main;2,0.5;5,1;]".. "list[context;main;2,0.5;5,1;]",
mcl_formspec.get_itemslot_bg(2,0.5,5,1).. mcl_formspec.get_itemslot_bg(2, 0.5, 5, 1),
"label[0,2;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "label[0,2;" .. F(C("#313131", S("Inventory"))) .. "]",
"list[current_player;main;0,2.5;9,3;9]".. "list[current_player;main;0,2.5;9,3;9]",
mcl_formspec.get_itemslot_bg(0,2.5,9,3).. mcl_formspec.get_itemslot_bg(0, 2.5, 9, 3),
"list[current_player;main;0,5.74;9,1;]".. "list[current_player;main;0,5.74;9,1;]",
mcl_formspec.get_itemslot_bg(0,5.74,9,1).. mcl_formspec.get_itemslot_bg(0, 5.74, 9, 1),
"listring[context;main]".. "listring[context;main]",
"listring[current_player;main]" "listring[current_player;main]",
})
-- Downwards hopper (base definition) -- Downwards hopper (base definition)
---@type node_definition
local def_hopper = { local def_hopper = {
inventory_image = "mcl_hoppers_item.png", inventory_image = "mcl_hoppers_item.png",
wield_image = "mcl_hoppers_item.png", wield_image = "mcl_hoppers_item.png",
groups = {pickaxey=1, container=2,deco_block=1,hopper=1}, groups = {pickaxey = 1, container = 2, deco_block = 1, hopper = 1},
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
-- FIXME: mcl_hoppers_hopper_inside.png is unused by hoppers. -- FIXME: mcl_hoppers_hopper_inside.png is unused by hoppers.
tiles = {"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"}, tiles = {
"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
},
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -71,10 +82,10 @@ local def_hopper = {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() local inv = meta:get_inventory()
for i=1,inv:get_size("main") do for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i) local stack = inv:get_stack("main", i)
if not stack:is_empty() then 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} local p = vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5)
minetest.add_item(p, stack) minetest.add_item(p, stack)
end end
end end
@ -108,16 +119,16 @@ local def_hopper = {
end end
end, end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos)) " moves stuff in mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_put = function(pos, listname, index, stack, player) on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos)) " moves stuff to mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_take = function(pos, listname, index, stack, player) on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos)) " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
sounds = mcl_sounds.node_sound_metal_defaults(), sounds = mcl_sounds.node_sound_metal_defaults(),
@ -131,18 +142,22 @@ local def_hopper = {
-- Enabled downwards hopper -- Enabled downwards hopper
local def_hopper_enabled = table.copy(def_hopper) local def_hopper_enabled = table.copy(def_hopper)
def_hopper_enabled.description = S("Hopper") def_hopper_enabled.description = S("Hopper")
def_hopper_enabled._tt_help = S("5 inventory slots").."\n"..S("Collects items from above, moves items to container below").."\n"..S("Can be disabled with redstone power") def_hopper_enabled._tt_help = S("5 inventory slots") ..
def_hopper_enabled._doc_items_longdesc = S("Hoppers are containers with 5 inventory slots. They collect dropped items from above, take items from a container above and attempt 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.").."\n\n".. "\n" .. S("Collects items from above, moves items to container below") .. "\n" ..
S("Can be disabled with redstone power")
def_hopper_enabled._doc_items_longdesc = S("Hoppers are containers with 5 inventory slots. They collect dropped items from above, take items from a container above and attempt 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.")
.. "\n\n" ..
S("Hoppers interact with containers the following way:").."\n".. S("Hoppers interact with containers the following way:") .. "\n" ..
S("• 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 that point to the furnace put items into the fuel slot").."\n".. S("• 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 that point to the furnace put items into the fuel slot")
S("• Ender chests: No interaction.").."\n".. .. "\n" ..
S("• Other containers: Normal interaction.").."\n\n".. S("• Ender chests: No interaction.") .. "\n" ..
S("• Other containers: Normal interaction.") .. "\n\n" ..
S("Hoppers can be disabled when supplied with redstone power. Disabled hoppers don't move items.") S("Hoppers can be disabled when supplied with redstone power. Disabled hoppers don't move items.")
def_hopper_enabled._doc_items_usagehelp = S("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. Use the hopper to access its inventory.") def_hopper_enabled._doc_items_usagehelp = S("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. Use the hopper to access its inventory.")
def_hopper_enabled.on_place = function(itemstack, placer, pointed_thing) def_hopper_enabled.on_place = function(itemstack, placer, pointed_thing)
local upos = pointed_thing.under local upos = pointed_thing.under
local apos = pointed_thing.above local apos = pointed_thing.above
local uposnode = minetest.get_node(upos) local uposnode = minetest.get_node(upos)
@ -173,21 +188,22 @@ def_hopper_enabled.on_place = function(itemstack, placer, pointed_thing)
fake_itemstack:set_name("mcl_hoppers:hopper_side") fake_itemstack:set_name("mcl_hoppers:hopper_side")
param2 = 1 param2 = 1
end end
local itemstack,_ = minetest.item_place_node(fake_itemstack, placer, pointed_thing, param2) local itemstack, _ = minetest.item_place_node(fake_itemstack, placer, pointed_thing, param2)
itemstack:set_name("mcl_hoppers:hopper") itemstack:set_name("mcl_hoppers:hopper")
return itemstack return itemstack
end end
def_hopper_enabled.mesecons = { def_hopper_enabled.mesecons = {
effector = { effector = {
action_on = function(pos, node) action_on = function(pos, node)
minetest.swap_node(pos, {name="mcl_hoppers:hopper_disabled", param2=node.param2}) minetest.swap_node(pos, {name = "mcl_hoppers:hopper_disabled", param2 = node.param2})
end, end,
}, },
} }
minetest.register_node("mcl_hoppers:hopper", def_hopper_enabled) minetest.register_node("mcl_hoppers:hopper", def_hopper_enabled)
-- Disabled downwards hopper ---Disabled downwards hopper
---@type node_definition
local def_hopper_disabled = table.copy(def_hopper) local def_hopper_disabled = table.copy(def_hopper)
def_hopper_disabled.description = S("Disabled Hopper") def_hopper_disabled.description = S("Disabled Hopper")
def_hopper_disabled.inventory_image = nil def_hopper_disabled.inventory_image = nil
@ -197,7 +213,7 @@ def_hopper_disabled.drop = "mcl_hoppers:hopper"
def_hopper_disabled.mesecons = { def_hopper_disabled.mesecons = {
effector = { effector = {
action_off = function(pos, node) action_off = function(pos, node)
minetest.swap_node(pos, {name="mcl_hoppers:hopper", param2=node.param2}) minetest.swap_node(pos, {name = "mcl_hoppers:hopper", param2 = node.param2})
end, end,
}, },
} }
@ -211,15 +227,23 @@ if minetest.get_modpath("screwdriver") then
on_rotate = screwdriver.rotate_simple on_rotate = screwdriver.rotate_simple
end end
-- Sidewars hopper (base definition) ---Sidewars hopper (base definition)
---@type node_definition
local def_hopper_side = { local def_hopper_side = {
_doc_items_create_entry = false, _doc_items_create_entry = false,
drop = "mcl_hoppers:hopper", drop = "mcl_hoppers:hopper",
groups = {pickaxey=1, container=2,not_in_creative_inventory=1,hopper=2}, groups = {pickaxey = 1, container = 2, not_in_creative_inventory = 1, hopper = 2},
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", 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_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"}, tiles = {
"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
"mcl_hoppers_hopper_outside.png",
},
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -259,10 +283,10 @@ local def_hopper_side = {
local meta2 = meta local meta2 = meta
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() local inv = meta:get_inventory()
for i=1,inv:get_size("main") do for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i) local stack = inv:get_stack("main", i)
if not stack:is_empty() then 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} local p = vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5)
minetest.add_item(p, stack) minetest.add_item(p, stack)
end end
end end
@ -296,16 +320,16 @@ local def_hopper_side = {
end end
end, end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos)) " moves stuff in mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_put = function(pos, listname, index, stack, player) on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos)) " moves stuff to mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_take = function(pos, listname, index, stack, player) on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos)) " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos))
end, end,
on_rotate = on_rotate, on_rotate = on_rotate,
sounds = mcl_sounds.node_sound_metal_defaults(), sounds = mcl_sounds.node_sound_metal_defaults(),
@ -314,23 +338,25 @@ local def_hopper_side = {
_mcl_hardness = 3, _mcl_hardness = 3,
} }
---@type node_definition
local def_hopper_side_enabled = table.copy(def_hopper_side) local def_hopper_side_enabled = table.copy(def_hopper_side)
def_hopper_side_enabled.description = S("Side Hopper") def_hopper_side_enabled.description = S("Side Hopper")
def_hopper_side_enabled.mesecons = { def_hopper_side_enabled.mesecons = {
effector = { effector = {
action_on = function(pos, node) action_on = function(pos, node)
minetest.swap_node(pos, {name="mcl_hoppers:hopper_side_disabled", param2=node.param2}) minetest.swap_node(pos, {name = "mcl_hoppers:hopper_side_disabled", param2 = node.param2})
end, end,
}, },
} }
minetest.register_node("mcl_hoppers:hopper_side", def_hopper_side_enabled) minetest.register_node("mcl_hoppers:hopper_side", def_hopper_side_enabled)
---@type node_definition
local def_hopper_side_disabled = table.copy(def_hopper_side) local def_hopper_side_disabled = table.copy(def_hopper_side)
def_hopper_side_disabled.description = S("Disabled Side Hopper") def_hopper_side_disabled.description = S("Disabled Side Hopper")
def_hopper_side_disabled.mesecons = { def_hopper_side_disabled.mesecons = {
effector = { effector = {
action_off = function(pos, node) action_off = function(pos, node)
minetest.swap_node(pos, {name="mcl_hoppers:hopper_side", param2=node.param2}) minetest.swap_node(pos, {name = "mcl_hoppers:hopper_side", param2 = node.param2})
end, end,
}, },
} }
@ -338,7 +364,7 @@ minetest.register_node("mcl_hoppers:hopper_side_disabled", def_hopper_side_disab
--[[ END OF NODE DEFINITIONS ]] --[[ END OF NODE DEFINITIONS ]]
local function hopper_pull_from_mc (mc_ent, dest_pos, inv_size) local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size)
local inv = mcl_entity_invs.load_inv(mc_ent, inv_size) local inv = mcl_entity_invs.load_inv(mc_ent, inv_size)
if not inv then if not inv then
mcl_log("No inv") mcl_log("No inv")
@ -353,7 +379,7 @@ local function hopper_pull_from_mc (mc_ent, dest_pos, inv_size)
end end
mcl_log("inv. size: " .. mc_ent._inv_size) mcl_log("inv. size: " .. mc_ent._inv_size)
for i = 1, mc_ent._inv_size,1 do for i = 1, mc_ent._inv_size, 1 do
local stack = inv:get_stack("main", i) local stack = inv:get_stack("main", i)
mcl_log("i: " .. tostring(i)) mcl_log("i: " .. tostring(i))
@ -382,8 +408,8 @@ end
--[[ BEGIN OF ABM DEFINITONS ]] --[[ BEGIN OF ABM DEFINITONS ]]
minetest.register_abm({ minetest.register_abm({
label = "Hoppers pull from minecart", label = "Hoppers pull from minecart hoppers",
nodenames = {"mcl_hoppers:hopper","mcl_hoppers:hopper_side"}, nodenames = {"mcl_hoppers:hopper", "mcl_hoppers:hopper_side"},
interval = 0.5, interval = 0.5,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) action = function(pos, node, active_object_count, active_object_count_wider)
@ -391,14 +417,14 @@ minetest.register_abm({
local objs = minetest.get_objects_inside_radius(pos, 3) local objs = minetest.get_objects_inside_radius(pos, 3)
if objs and #objs > 0 then if objs and #objs > 0 then
for k,v in pairs(objs) do for k, v in pairs(objs) do
local entity = v:get_luaentity() local entity = v:get_luaentity()
if entity and entity.name then if entity and entity.name then
--mcl_log("Name of object near: " .. tostring(entity.name)) --mcl_log("Name of object near: " .. tostring(entity.name))
if entity.name == "mcl_minecarts:hopper_minecart" or entity.name == "mcl_minecarts:chest_minecart"then if entity.name == "mcl_minecarts:hopper_minecart" or entity.name == "mcl_minecarts:chest_minecart" then
local hm_pos = entity.object:get_pos() local hm_pos = entity.object:get_pos()
mcl_log("We have a minecart with inventory close: ".. minetest.pos_to_string(hm_pos)) mcl_log("We have a minecart with inventory close: " .. minetest.pos_to_string(hm_pos))
--if hm_pos.y == pos.y + 1 then mcl_log("y is correct") end --if hm_pos.y == pos.y + 1 then mcl_log("y is correct") end
--if (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC) then mcl_log("x is within range") end --if (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC) then mcl_log("x is within range") end
@ -406,8 +432,8 @@ minetest.register_abm({
local DIST_FROM_MC = 1.5 local DIST_FROM_MC = 1.5
if (hm_pos.y == pos.y + 1) if (hm_pos.y == pos.y + 1)
and (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC) and (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC)
and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then
mcl_log("Minecart close enough") mcl_log("Minecart close enough")
if entity.name == "mcl_minecarts:hopper_minecart" then if entity.name == "mcl_minecarts:hopper_minecart" then
hopper_pull_from_mc(entity, pos, 5) hopper_pull_from_mc(entity, pos, 5)
@ -429,11 +455,11 @@ minetest.register_abm({
-- Make hoppers suck in dropped items -- Make hoppers suck in dropped items
minetest.register_abm({ minetest.register_abm({
label = "Hoppers suck in dropped items", label = "Hoppers suck in dropped items",
nodenames = {"mcl_hoppers:hopper","mcl_hoppers:hopper_side"}, nodenames = {"mcl_hoppers:hopper", "mcl_hoppers:hopper_side"},
interval = 1.0, interval = 1.0,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) 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}) local abovenode = minetest.get_node(vector.offset(pos, 0, 1, 0))
if not minetest.registered_items[abovenode.name] then return end 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) -- Don't bother checking item enties if node above is a container (should save some CPU)
if minetest.get_item_group(abovenode.name, "container") ~= 0 then if minetest.get_item_group(abovenode.name, "container") ~= 0 then
@ -442,15 +468,16 @@ minetest.register_abm({
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
for _,object in pairs(minetest.get_objects_inside_radius(pos, 2)) do for _, object in pairs(minetest.get_objects_inside_radius(pos, 2)) do
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and not object:get_luaentity()._removed then if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and
not object:get_luaentity()._removed then
if inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) 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 -- Item must get sucked in when the item just TOUCHES the block above the hopper
-- This is the reason for the Y calculation. -- This is the reason for the Y calculation.
-- Test: Items on farmland and slabs get sucked, but items on full blocks don't -- Test: Items on farmland and slabs get sucked, but items on full blocks don't
local posob = object:get_pos() local posob = object:get_pos()
local posob_miny = posob.y + object:get_properties().collisionbox[2] 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 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)) inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
object:get_luaentity().itemstring = "" object:get_luaentity().itemstring = ""
object:remove() object:remove()
@ -461,8 +488,14 @@ minetest.register_abm({
end, end,
}) })
-- Returns true if itemstack is fuel, but not for lava bucket if destination already has one ---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) ---@param itemstack ItemStack
---@param src_inventory InvRef
---@param src_list string
---@param dst_inventory InvRef
---@param dst_list string
---@return boolean
local function is_transferrable_fuel(itemstack, src_inventory, src_list, dst_inventory, dst_list)
if mcl_util.is_fuel(itemstack) then if mcl_util.is_fuel(itemstack) then
if itemstack:get_name() == "mcl_buckets:bucket_lava" then if itemstack:get_name() == "mcl_buckets:bucket_lava" then
return dst_inventory:is_empty(dst_list) return dst_inventory:is_empty(dst_list)
@ -474,8 +507,6 @@ local is_transferrable_fuel = function(itemstack, src_inventory, src_list, dst_i
end end
end end
minetest.register_abm({ minetest.register_abm({
label = "Hopper/container item exchange", label = "Hopper/container item exchange",
nodenames = {"mcl_hoppers:hopper"}, nodenames = {"mcl_hoppers:hopper"},
@ -484,8 +515,8 @@ minetest.register_abm({
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) action = function(pos, node, active_object_count, active_object_count_wider)
-- Get node pos' for item transfer -- Get node pos' for item transfer
local uppos = {x=pos.x,y=pos.y+1,z=pos.z} local uppos = vector.offset(pos, 0, 1, 0)
local downpos = {x=pos.x,y=pos.y-1,z=pos.z} local downpos = vector.offset(pos, 0, -1, 0)
-- Suck an item from the container above into the hopper -- Suck an item from the container above into the hopper
local upnode = minetest.get_node(uppos) local upnode = minetest.get_node(uppos)
@ -495,7 +526,7 @@ minetest.register_abm({
-- Also suck in non-fuel items from furnace fuel slot -- Also suck in non-fuel items from furnace fuel slot
if not sucked and g == 4 then if not sucked and g == 4 then
local finv = minetest.get_inventory({type="node", pos=uppos}) local finv = minetest.get_inventory({type = "node", pos = uppos})
if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then
mcl_util.move_item_container(uppos, pos, "fuel") mcl_util.move_item_container(uppos, pos, "fuel")
end end
@ -519,15 +550,15 @@ minetest.register_abm({
local face = minetest.get_node(pos).param2 local face = minetest.get_node(pos).param2
local front = {} local front = {}
if face == 0 then if face == 0 then
front = {x=pos.x-1,y=pos.y,z=pos.z} front = vector.offset(pos, -1, 0, 0)
elseif face == 1 then elseif face == 1 then
front = {x=pos.x,y=pos.y,z=pos.z+1} front = vector.offset(pos, 0, 0, 1)
elseif face == 2 then elseif face == 2 then
front = {x=pos.x+1,y=pos.y,z=pos.z} front = vector.offset(pos, 1, 0, 0)
elseif face == 3 then elseif face == 3 then
front = {x=pos.x,y=pos.y,z=pos.z-1} front = vector.offset(pos, 0, 0, -1)
end end
local above = {x=pos.x,y=pos.y+1,z=pos.z} local above = vector.offset(pos, 0, 1, 0)
local frontnode = minetest.get_node(front) local frontnode = minetest.get_node(front)
if not minetest.registered_nodes[frontnode.name] then return end if not minetest.registered_nodes[frontnode.name] then return end
@ -540,7 +571,7 @@ minetest.register_abm({
-- Also suck in non-fuel items from furnace fuel slot -- Also suck in non-fuel items from furnace fuel slot
if not sucked and g == 4 then if not sucked and g == 4 then
local finv = minetest.get_inventory({type="node", pos=above}) local finv = minetest.get_inventory({type = "node", pos = above})
if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then
mcl_util.move_item_container(above, pos, "fuel") mcl_util.move_item_container(above, pos, "fuel")
end end
@ -552,9 +583,9 @@ minetest.register_abm({
mcl_util.move_item_container(pos, front) mcl_util.move_item_container(pos, front)
elseif g == 4 then elseif g == 4 then
-- Put fuel into fuel slot -- Put fuel into fuel slot
local sinv = minetest.get_inventory({type="node", pos = pos}) local sinv = minetest.get_inventory({type = "node", pos = pos})
local dinv = minetest.get_inventory({type="node", pos = front}) local dinv = minetest.get_inventory({type = "node", pos = front})
local slot_id,_ = mcl_util.get_eligible_transfer_item_slot(sinv, "main", dinv, "fuel", is_transferrable_fuel) local slot_id, _ = mcl_util.get_eligible_transfer_item_slot(sinv, "main", dinv, "fuel", is_transferrable_fuel)
if slot_id then if slot_id then
mcl_util.move_item_container(pos, front, nil, slot_id, "fuel") mcl_util.move_item_container(pos, front, nil, slot_id, "fuel")
end end
@ -562,110 +593,124 @@ minetest.register_abm({
end end
}) })
minetest.register_abm({ if minetest.get_modpath("mcl_composters") then
label = "Bonemeal extraction from composter", minetest.register_abm({
nodenames = {"mcl_hoppers:hopper", "mcl_hoppers:hopper_side"}, label = "Bonemeal extraction from composter",
neighbors = {"mcl_composters:composter_ready"}, nodenames = {"mcl_hoppers:hopper", "mcl_hoppers:hopper_side"},
interval = 1.0, neighbors = {"mcl_composters:composter_ready"},
chance = 1, interval = 1.0,
action = function(pos, node, active_object_count, active_object_count_wider) chance = 1,
local uppos = {x=pos.x,y=pos.y+1,z=pos.z} action = function(pos, node, active_object_count, active_object_count_wider)
local downpos = {x=pos.x,y=pos.y-1,z=pos.z} local uppos = vector.offset(pos, 0, 1, 0)
local meta = minetest.get_meta(pos) --local downpos = vector.offset(pos, 0, -1, 0)
local inv = meta:get_inventory()
-- Get bonemeal from composter above
local upnode = minetest.get_node(uppos)
if upnode.name == "mcl_composters:composter_ready" then
minetest.swap_node(uppos, {name="mcl_composters:composter"})
inv:add_item("main", "mcl_dye:white")
end
end,
})
minetest.register_abm({ -- Get bonemeal from composter above
label = "Add compostable items on composter", local upnode = minetest.get_node(uppos)
nodenames = {"mcl_hoppers:hopper"}, if upnode.name == "mcl_composters:composter_ready" then
neighbors = {"mcl_composters:composter", "mcl_composters:composter_1", "mcl_composters:composter_2", local meta = minetest.get_meta(pos)
"mcl_composters:composter_3", "mcl_composters:composter_4", "mcl_composters:composter_5", local inv = meta:get_inventory()
"mcl_composters:composter_6", "mcl_composters:composter_7",},
interval = 1.0, minetest.swap_node(uppos, {name = "mcl_composters:composter"})
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) inv:add_item("main", "mcl_dye:white")
local uppos = {x=pos.x,y=pos.y+1,z=pos.z} end
local downpos = {x=pos.x,y=pos.y-1,z=pos.z} end,
local meta = minetest.get_meta(pos) })
local inv = meta:get_inventory()
--Consume compostable items and update composter below ---@param node node
local downnode = minetest.get_node(downpos) ---@return integer?
if downnode.name == "mcl_composters:composter" or downnode.name == "mcl_composters:composter_1" ---@nodiscard
or downnode.name == "mcl_composters:composter_2" or downnode.name == "mcl_composters:composter_3" local function composter_level(node)
or downnode.name == "mcl_composters:composter_4" or downnode.name == "mcl_composters:composter_5" local nn = node.name
or downnode.name == "mcl_composters:composter_6" or downnode.name == "mcl_composters:composter_7" then if nn == "mcl_composters:composter" then
local itemcomp = inv:get_list("main") return 0
local hslot = mcl_util.get_first_occupied_inventory_slot(minetest.get_inventory({type="node", pos = pos}), "main") elseif nn == "mcl_composters:composter_1" then
if hslot == nil then return end return 1
local compchance = minetest.get_item_group(itemcomp[hslot]:get_name(), "compostability") elseif nn == "mcl_composters:composter_2" then
if compchance == 0 then return 2
hslot = hslot+1 elseif nn == "mcl_composters:composter_3" then
if hslot == 6 then return end return 3
compchance = minetest.get_item_group(itemcomp[hslot]:get_name(), "compostability") elseif nn == "mcl_composters:composter_4" then
if compchance == 0 then return 4
hslot = hslot+1 elseif nn == "mcl_composters:composter_5" then
if hslot == 6 then return end return 5
compchance = minetest.get_item_group(itemcomp[hslot]:get_name(), "compostability") elseif nn == "mcl_composters:composter_6" then
if compchance == 0 then return 6
hslot = hslot+1 elseif nn == "mcl_composters:composter_7" then
if hslot == 6 then return end return 7
compchance = minetest.get_item_group(itemcomp[hslot]:get_name(), "compostability") else
if compchance == 0 then return nil
hslot = hslot+1 end
if hslot == 6 then return end end
compchance = minetest.get_item_group(itemcomp[hslot]:get_name(), "compostability")
end for i = 1, 7 do
end assert(composter_level({name = "mcl_composters:composter_" .. i}) == i)
end end
end
if compchance > 0 then assert(composter_level({name = "mcl_composters:composter"}) == 0)
itemcomp[hslot]:take_item() assert(composter_level({name = "mcl_composters:some_other_node"}) == nil)
inv:set_list("main", itemcomp)
local rand = math.random(0,100) minetest.register_abm({
if compchance >= rand then label = "Add compostable items on composter",
local level = 0 nodenames = {"mcl_hoppers:hopper"},
if downnode.name == "mcl_composters:composter_1" then neighbors = {
level = 1 "mcl_composters:composter",
elseif downnode.name == "mcl_composters:composter_2" then "mcl_composters:composter_1",
level = 2 "mcl_composters:composter_2",
elseif downnode.name == "mcl_composters:composter_3" then "mcl_composters:composter_3",
level = 3 "mcl_composters:composter_4",
elseif downnode.name == "mcl_composters:composter_4" then "mcl_composters:composter_5",
level = 4 "mcl_composters:composter_6",
elseif downnode.name == "mcl_composters:composter_5" then "mcl_composters:composter_7",
level = 5 },
elseif downnode.name == "mcl_composters:composter_6" then interval = 1.0,
level = 6 chance = 1,
elseif downnode.name == "mcl_composters:composter_7" then action = function(pos, node, active_object_count, active_object_count_wider)
level = 7 --local uppos = vector.offset(pos, 0, 1, 0)
end local downpos = vector.offset(pos, 0, -1, 0)
mcl_dye.add_bone_meal_particle(vector.offset(downpos, 0, level/8, 0))
if level < 7 then local downnode = minetest.get_node(downpos)
level = level + 1
else ---@type integer|string|nil
level = "ready" local level = composter_level(downnode)
end
minetest.swap_node(downpos, {name="mcl_composters:composter_" .. level}) --Consume compostable items and update composter below
end if level then
end local meta = minetest.get_meta(pos)
end local inv = meta:get_inventory()
end,
}) for i = 1, 5 do
local stack = inv:get_stack("main", i)
local compchance = minetest.get_item_group(stack:get_name(), "compostability")
if compchance > 0 then
stack:take_item()
inv:set_stack("main", i, stack)
if compchance >= math.random(0, 100) then
mcl_dye.add_bone_meal_particle(vector.offset(downpos, 0, level / 8, 0))
if level < 7 then
level = level + 1
else
level = "ready"
end
minetest.swap_node(downpos, {name = "mcl_composters:composter_" .. level})
end
break
end
end
end
end,
})
end
minetest.register_craft({ minetest.register_craft({
output = "mcl_hoppers:hopper", output = "mcl_hoppers:hopper",
recipe = { recipe = {
{"mcl_core:iron_ingot","","mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
{"mcl_core:iron_ingot","mcl_chests:chest","mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "mcl_chests:chest", "mcl_core:iron_ingot"},
{"","mcl_core:iron_ingot",""}, {"", "mcl_core:iron_ingot", ""},
} },
}) })
-- Add entry aliases for the Help -- Add entry aliases for the Help
@ -679,7 +724,7 @@ minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")
minetest.register_lbm({ minetest.register_lbm({
label = "Update hopper formspecs (0.60.0", label = "Update hopper formspecs (0.60.0",
name = "mcl_hoppers:update_formspec_0_60_0", name = "mcl_hoppers:update_formspec_0_60_0",
nodenames = { "group:hopper" }, nodenames = {"group:hopper"},
run_at_every_load = false, run_at_every_load = false,
action = function(pos, node) action = function(pos, node)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)

@ -1,4 +1,4 @@
name = mcl_hoppers name = mcl_hoppers
description = It's just a clone of Minecraft hoppers, functions nearly identical to them minus mesecons making them stop and the way they're placed. description = It's just a clone of Minecraft hoppers, functions nearly identical to them minus mesecons making them stop and the way they're placed.
depends = mcl_core, mcl_formspec, mcl_sounds, mcl_util depends = mcl_core, mcl_formspec, mcl_sounds, mcl_util, mcl_dye
optional_depends = doc, screwdriver optional_depends = doc, screwdriver