Merge pull request 'Fix #4189 - Make hoppers move items if there is space for one item' (#4190) from teknomunk/MineClone2:hopper-changes into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/4190
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
This commit is contained in:
the-real-herowl 2024-04-27 13:51:34 +00:00
commit 4e12c6747c
7 changed files with 133 additions and 92 deletions

View file

@ -257,12 +257,21 @@ end
---@param dst_inventory InvRef Destination inventory to push to ---@param dst_inventory InvRef Destination inventory to push to
---@param dst_list string Name of destination inventory list to push to ---@param dst_list string Name of destination inventory list to push to
---@param condition? fun(stack: ItemStack) Condition which items are allowed to be transfered. ---@param condition? fun(stack: ItemStack) Condition which items are allowed to be transfered.
---@param count? integer Number of items to try to transfer at once
---@return integer Item stack number to be transfered ---@return integer Item stack number to be transfered
function mcl_util.select_stack(src_inventory, src_list, dst_inventory, dst_list, condition) function mcl_util.select_stack(src_inventory, src_list, dst_inventory, dst_list, condition, count)
local src_size = src_inventory:get_size(src_list) local src_size = src_inventory:get_size(src_list)
local stack local stack
for i = 1, src_size do for i = 1, src_size do
stack = src_inventory:get_stack(src_list, i) stack = src_inventory:get_stack(src_list, i)
-- Allow for partial stack movement
if count and stack:get_count() >= count then
local new_stack = ItemStack(stack)
new_stack:set_count(count)
stack = new_stack
end
if not stack:is_empty() and dst_inventory:room_for_item(dst_list, stack) and ((condition == nil or condition(stack))) then if not stack:is_empty() and dst_inventory:room_for_item(dst_list, stack) and ((condition == nil or condition(stack))) then
return i return i
end end
@ -280,21 +289,22 @@ end
-- Returns true on success and false on failure -- Returns true on success and false on failure
-- Possible failures: No item in source slot, destination inventory full -- Possible failures: No item in source slot, destination inventory full
function mcl_util.move_item(source_inventory, source_list, source_stack_id, destination_inventory, destination_list) function mcl_util.move_item(source_inventory, source_list, source_stack_id, destination_inventory, destination_list)
if not source_inventory:is_empty(source_list) then -- Can't move items we don't have
local stack = source_inventory:get_stack(source_list, source_stack_id) if source_inventory:is_empty(source_list) then return false end
if not stack:is_empty() then
local new_stack = ItemStack(stack) -- Can't move from an empty stack
new_stack:set_count(1) local stack = source_inventory:get_stack(source_list, source_stack_id)
if not destination_inventory:room_for_item(destination_list, new_stack) then if stack:is_empty() then return false end
return false
end local new_stack = ItemStack(stack)
stack:take_item() new_stack:set_count(1)
source_inventory:set_stack(source_list, source_stack_id, stack) if not destination_inventory:room_for_item(destination_list, new_stack) then
destination_inventory:add_item(destination_list, new_stack) return false
return true
end
end end
return false stack:take_item()
source_inventory:set_stack(source_list, source_stack_id, stack)
destination_inventory:add_item(destination_list, new_stack)
return true
end end
--- Try pushing item from hopper inventory to destination inventory --- Try pushing item from hopper inventory to destination inventory
@ -314,25 +324,23 @@ function mcl_util.hopper_push(pos, dst_pos)
local dst_list = 'main' local dst_list = 'main'
local dst_inv, stack_id local dst_inv, stack_id
-- Find a inventory stack in the destination
if dst_def._mcl_hoppers_on_try_push then if dst_def._mcl_hoppers_on_try_push then
dst_inv, dst_list, stack_id = dst_def._mcl_hoppers_on_try_push(dst_pos, pos, hop_inv, hop_list) dst_inv, dst_list, stack_id = dst_def._mcl_hoppers_on_try_push(dst_pos, pos, hop_inv, hop_list)
else else
local dst_meta = minetest.get_meta(dst_pos) local dst_meta = minetest.get_meta(dst_pos)
dst_inv = dst_meta:get_inventory() dst_inv = dst_meta:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, dst_inv, dst_list) stack_id = mcl_util.select_stack(hop_inv, hop_list, dst_inv, dst_list, nil, 1)
end
if not stack_id then return false end
-- Move the item
local ok = mcl_util.move_item(hop_inv, hop_list, stack_id, dst_inv, dst_list)
if dst_def._mcl_hoppers_on_after_push then
dst_def._mcl_hoppers_on_after_push(dst_pos)
end end
if stack_id ~= nil then return ok
local ok = mcl_util.move_item(hop_inv, hop_list, stack_id, dst_inv, dst_list)
if dst_def._mcl_hoppers_on_after_push then
dst_def._mcl_hoppers_on_after_push(dst_pos)
end
if ok then
return true
end
end
return false
end end
-- Try pulling from source inventory to hopper inventory -- Try pulling from source inventory to hopper inventory
@ -357,7 +365,7 @@ function mcl_util.hopper_pull(pos, src_pos)
else else
local src_meta = minetest.get_meta(src_pos) local src_meta = minetest.get_meta(src_pos)
src_inv = src_meta:get_inventory() src_inv = src_meta:get_inventory()
stack_id = mcl_util.select_stack(src_inv, src_list, hop_inv, hop_list) stack_id = mcl_util.select_stack(src_inv, src_list, hop_inv, hop_list, nil, 1)
end end
if stack_id ~= nil then if stack_id ~= nil then

View file

@ -475,8 +475,11 @@ minetest.register_node("mcl_books:bookshelf", {
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", local function filter(stack)
function(stack) return minetest.get_item_group(stack:get_name(), "book") == 1 or stack:get_name() == "mcl_enchanting:book_enchanted" end) return minetest.get_item_group(stack:get_name(), "book") == 1 or
stack:get_name() == "mcl_enchanting:book_enchanted"
end
return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", filter, 1)
end, end,
}) })

View file

@ -379,16 +379,27 @@ end
local function hoppers_on_try_push(pos, hop_pos, hop_inv, hop_list) local function hoppers_on_try_push(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z) then
return inv, "input", mcl_util.select_stack(hop_inv, hop_list, inv, "input", if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z)
function(stack) return minetest.get_item_group(stack:get_name(), "brewitem") == 1 and minetest.get_item_group(stack:get_name(), "bottle") == 0 end) then
local function filter(stack)
return minetest.get_item_group(stack:get_name(), "brewitem") == 1 and
minetest.get_item_group(stack:get_name(), "bottle") == 0
end
return inv, "input", mcl_util.select_stack(hop_inv, hop_list, inv, "input", filter, 1)
else else
local stack = mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", function(stack) return stack:get_name() == "mcl_mobitems:blaze_powder" end) local filter = function(stack)
return stack:get_name() == "mcl_mobitems:blaze_powder"
end
local stack = mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", filter, 1 )
if stack then if stack then
return inv, "fuel", stack return inv, "fuel", stack
else else
return inv, "stand", mcl_util.select_stack(hop_inv, hop_list, inv, "stand", local function filter(stack)
function(stack) return minetest.get_item_group(stack:get_name(), "bottle") == 1 end) return minetest.get_item_group(stack:get_name(), "bottle") == 1
end
return inv, "stand", mcl_util.select_stack(hop_inv, hop_list, inv, "stand", filter, 1)
end end
end end
end end

View file

@ -760,7 +760,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main") local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1)
if stack_id ~= nil then if stack_id ~= nil then
return inv, "main", stack_id return inv, "main", stack_id
end end
@ -768,7 +768,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
local meta_other = minetest.get_meta(pos_other) local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory() local inv_other = meta_other:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main") stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1)
return inv_other, "main", stack_id return inv_other, "main", stack_id
end, end,
}) })
@ -955,13 +955,13 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
local meta_other = minetest.get_meta(pos_other) local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory() local inv_other = meta_other:get_inventory()
local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main") local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1)
if stack_id ~= nil then if stack_id ~= nil then
return inv_other, "main", stack_id return inv_other, "main", stack_id
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main") stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1)
return inv, "main", stack_id return inv, "main", stack_id
end, end,
}) })
@ -1522,7 +1522,7 @@ for color, desc in pairs(boxtypes) do
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box) return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box, 1)
end, end,
}) })

View file

@ -238,7 +238,7 @@ minetest.register_node("mcl_composters:composter", {
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition) return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition, 1)
end, end,
_mcl_hoppers_on_after_push = function(pos) _mcl_hoppers_on_after_push = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
@ -293,7 +293,7 @@ local function register_filled_composter(level)
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition) return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition, 1)
end, end,
_mcl_hoppers_on_after_push = function(pos) _mcl_hoppers_on_after_push = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)

View file

@ -466,9 +466,9 @@ function mcl_furnaces.hoppers_on_try_push(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z) then if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z) then
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src") return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", nil, 1)
else else
return inv, "fuel", mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", mcl_util.is_fuel) return inv, "fuel", mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", mcl_util.is_fuel, 1)
end end
end end

View file

@ -32,6 +32,68 @@ local mcl_hoppers_formspec = table.concat({
"listring[current_player;main]", "listring[current_player;main]",
}) })
local function straight_hopper_act(pos, node, active_object_count, active_count_wider)
local timer = minetest.get_node_timer(pos)
if timer:is_started() then
--Pause if already recived item this tick
return
end
timer:start(1.0)
-- Move from internal inventory to dst first
local dst_pos = vector.offset(pos, 0, -1, 0)
local dst_node = minetest.get_node(dst_pos)
local dst_name = dst_node.name
local dst_def = minetest.registered_nodes[dst_name]
if dst_def and dst_def._mcl_hopper_act then
dst_def._mcl_hopper_act( dst_pos, dst_node, active_object_count, active_count_wider )
end
mcl_util.hopper_push(pos, dst_pos)
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
end
local function bent_hopper_act(pos, node, active_object_count, active_object_count_wider)
local timer = minetest.get_node_timer(pos)
if timer:is_started() then
--Pause if already recived item this tick
return
end
timer:start(1.0)
-- Check if we are empty
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local empty = inv:is_empty("main")
-- Determine to which side the hopper is facing, get nodes
local face = minetest.get_node(pos).param2
local dst_pos = {}
if face == 0 then
dst_pos = vector.offset(pos, -1, 0, 0)
elseif face == 1 then
dst_pos = vector.offset(pos, 0, 0, 1)
elseif face == 2 then
dst_pos = vector.offset(pos, 1, 0, 0)
elseif face == 3 then
dst_pos = vector.offset(pos, 0, 0, -1)
end
local dst_node = minetest.get_node(dst_pos)
local dst_name = dst_node.name
local dst_def = minetest.registered_nodes[dst_name]
if dst_def and dst_def._mcl_hopper_act then
dst_def._mcl_hopper_act( dst_pos, dst_node, active_object_count, active_object_count_wider )
end
if not empty then
mcl_util.hopper_push(pos, dst_pos)
end
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
end
-- Downwards hopper (base definition) -- Downwards hopper (base definition)
---@type node_definition ---@type node_definition
@ -206,6 +268,7 @@ def_hopper_enabled.mesecons = {
end, end,
}, },
} }
def_hopper_enabled._mcl_hopper_act = straight_hopper_act
minetest.register_node("mcl_hoppers:hopper", def_hopper_enabled) minetest.register_node("mcl_hoppers:hopper", def_hopper_enabled)
@ -355,6 +418,7 @@ def_hopper_side_enabled.mesecons = {
end, end,
}, },
} }
def_hopper_side_enabled._mcl_hopper_act = bent_hopper_act
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 ---@type node_definition
@ -559,24 +623,7 @@ minetest.register_abm({
neighbors = { "group:container" }, neighbors = { "group:container" },
interval = 1.0, interval = 1.0,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) action = straight_hopper_act,
if minetest.get_node_timer(pos):is_started() then
return
end
-- Move from internal inventory to dst first
local dst_pos = vector.offset(pos, 0, -1, 0)
local pushed = mcl_util.hopper_push(pos, dst_pos)
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
local dst_node = minetest.get_node(dst_pos)
if pushed and (dst_node.name == "mcl_hoppers:hopper" or dst_node.name == "mcl_hoppers:hopper_side") then
--Pause destination hopper
minetest.get_node_timer(dst_pos):start(1.0)
end
end,
}) })
-- Register push/pull for "bent" hopper -- Register push/pull for "bent" hopper
@ -586,35 +633,7 @@ minetest.register_abm({
neighbors = { "group:container" }, neighbors = { "group:container" },
interval = 1.0, interval = 1.0,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) action = bent_hopper_act,
if minetest.get_node_timer(pos):is_started() then
--Pause if already recived item this tick
return
end
-- Determine to which side the hopper is facing, get nodes
local face = minetest.get_node(pos).param2
local dst_pos = {}
if face == 0 then
dst_pos = vector.offset(pos, -1, 0, 0)
elseif face == 1 then
dst_pos = vector.offset(pos, 0, 0, 1)
elseif face == 2 then
dst_pos = vector.offset(pos, 1, 0, 0)
elseif face == 3 then
dst_pos = vector.offset(pos, 0, 0, -1)
end
local pushed = mcl_util.hopper_push(pos, dst_pos)
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
local dst_node = minetest.get_node(dst_pos)
if pushed and (dst_node.name == "mcl_hoppers:hopper" or dst_node.name == "mcl_hoppers:hopper_side") then
--Pause destination hopper
minetest.get_node_timer(dst_pos):start(1.0)
end
end,
}) })
minetest.register_craft({ minetest.register_craft({