From 434010bdd0362b79a67a07a2055d690c50d9311f Mon Sep 17 00:00:00 2001 From: Christopher Head Date: Fri, 20 Oct 2017 00:24:32 -0700 Subject: [PATCH] Implement slot tracking Whenever putting or taking items, tell the listeners which slot they were put into or taken from. Also tell listeners when the user moves items from one slot to another. --- inventory.lua | 135 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 17 deletions(-) diff --git a/inventory.lua b/inventory.lua index 88cb6be..86b1536 100644 --- a/inventory.lua +++ b/inventory.lua @@ -4,12 +4,16 @@ local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil -- pos: the position of the Digilines chest node. -- action: the action string indicating what happened. -- stack: the ItemStack that the action acted on (optional). +-- from_slot: the slot number that is taken from (optional). +-- to_slot: the slot number that is put into (optional). -- side: which side of the chest the action occurred (optional). -local function send_message(pos, action, stack, side) +local function send_message(pos, action, stack, from_slot, to_slot, side) local channel = minetest.get_meta(pos):get_string("channel") local msg = { action = action, stack = stack and stack:to_table(), + from_slot = from_slot, + to_slot = to_slot, -- Duplicate the vector in case the caller expects it not to change. side = side and vector.new(side) } @@ -113,27 +117,88 @@ minetest.register_node("digilines:chest", { -- Here, direction = direction item is moving, which is into -- side. local side = vector.multiply(direction, -1) - send_message(pos, "toverflow", stack, side) + send_message(pos, "toverflow", stack, nil, nil, side) end return ret end, - insert_object = function(pos, _, stack, direction) + insert_object = function(pos, _, original_stack, direction) -- Here, direction = direction item is moving, which is into side. local side = vector.multiply(direction, -1) - local leftover = minetest.get_meta(pos):get_inventory():add_item("main", stack) - local leftover_count = leftover:get_count() + local inv = minetest.get_meta(pos):get_inventory() + local inv_contents = inv:get_list("main") + local any_put = false + local stack = original_stack + local stack_name = stack:get_name() local stack_count = stack:get_count() - if leftover_count ~= stack_count then - -- Some items were added. Report them. - stack:set_count(stack_count - leftover_count) - send_message(pos, "tput", stack, side) - check_full(pos, stack) + -- Walk the inventory, adding items to existing stacks of the same + -- type. + for i = 1, #inv_contents do + local existing_stack = inv_contents[i] + if not existing_stack:is_empty() and existing_stack:get_name() == stack_name then + local leftover = existing_stack:add_item(stack) + local leftover_count = leftover:get_count() + if leftover_count ~= stack_count then + -- We put some items into the slot. Update the slot in + -- the inventory, tell Digilines listeners about it, + -- and keep looking for the a place to put the + -- leftovers if any. + any_put = true + inv:set_stack("main", i, existing_stack) + local stack_that_was_put + if leftover_count == 0 then + stack_that_was_put = stack + else + stack_that_was_put = ItemStack(stack) + stack_that_was_put:set_count(stack_count - leftover_count) + end + send_message(pos, "tput", stack_that_was_put, nil, i, side) + stack = leftover + stack_count = leftover_count + if stack_count == 0 then + break + end + end + end end - if leftover_count ~= 0 then + if stack_count ~= 0 then + -- Walk the inventory, adding items to empty slots. + for i = 1, #inv_contents do + local existing_stack = inv_contents[i] + if existing_stack:is_empty() then + local leftover = existing_stack:add_item(stack) + local leftover_count = leftover:get_count() + if leftover_count ~= stack_count then + -- We put some items into the slot. Update the slot in + -- the inventory, tell Digilines listeners about it, + -- and keep looking for the a place to put the + -- leftovers if any. + any_put = true + inv:set_stack("main", i, existing_stack) + local stack_that_was_put + if leftover_count == 0 then + stack_that_was_put = stack + else + stack_that_was_put = ItemStack(stack) + stack_that_was_put:set_count(stack_count - leftover_count) + end + send_message(pos, "tput", stack_that_was_put, nil, i, side) + stack = leftover + stack_count = leftover_count + if stack_count == 0 then + break + end + end + end + end + end + if any_put then + check_full(pos, original_stack) + end + if stack_count ~= 0 then -- Some items could not be added and bounced back. Report them. - send_message(pos, "toverflow", leftover, side) + send_message(pos, "toverflow", stack, nil, nil, side) end - return leftover + return stack end, remove_items = function(pos, _, stack, dir, count) -- Here, stack is the ItemStack in our own inventory that is being @@ -147,7 +212,7 @@ minetest.register_node("digilines:chest", { -- index. local taken = stack:take_item(count) minetest.get_meta(pos):get_inventory():set_stack("main", last_inventory_take_index, stack) - send_message(pos, "ttake", taken, dir) + send_message(pos, "ttake", taken, last_inventory_take_index, nil, dir) check_empty(pos) return taken end, @@ -165,6 +230,42 @@ minetest.register_node("digilines:chest", { return stack:get_count() end, on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + -- See what would happen if we were to move the items back from in the + -- opposite direction. In the event of a normal move, this must + -- succeed, because a normal move subtracts some items from the from + -- stack and adds them to the to stack; the two stacks naturally must + -- be compatible and so the reverse operation must succeed. However, if + -- the user *swaps* the two stacks instead, then due to issue + -- minetest/minetest#6534, this function is only called once; however, + -- when it is called, the stack that used to be in the to stack has + -- already been moved to the from stack, so we can detect the situation + -- by the fact that the reverse move will fail due to the from stack + -- being incompatible with its former contents. + local inv = minetest.get_meta(pos):get_inventory() + local from_stack = inv:get_stack("main", from_index) + local to_stack = inv:get_stack("main", to_index) + local reverse_move_stack = ItemStack(to_stack) + reverse_move_stack:set_count(count) + local swapped = from_stack:add_item(reverse_move_stack):get_count() == count + if swapped then + local channel = minetest.get_meta(pos):get_string("channel") + to_stack:set_count(count) + local msg = { + action = "uswap", + -- The slot and stack do not match because this function is + -- called after the action has taken place, but the Digilines + -- message is from the perspective of a viewer who hasn’t + -- observed the movement yet. + x_stack = to_stack:to_table(), + x_slot = from_index, + y_stack = from_stack:to_table(), + y_slot = to_index, + } + digilines.receptor_send(pos, digilines.rules.default, channel, msg) + else + to_stack:set_count(count) + send_message(pos, "umove", to_stack, from_index, to_index) + end minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_put = function(pos, _, index, stack, player) @@ -196,14 +297,14 @@ minetest.register_node("digilines:chest", { -- Digilines network as a utake followed by a uput. local leftovers = old_stack:add_item(stack) if leftovers:get_count() == stack:get_count() then - send_message(pos, "utake", old_stack) + send_message(pos, "utake", old_stack, index) end - send_message(pos, "uput", stack) + send_message(pos, "uput", stack, nil, index) check_full(pos, stack) minetest.log("action", player:get_player_name().." puts stuff into chest at "..minetest.pos_to_string(pos)) end, on_metadata_inventory_take = function(pos, _, index, stack, player) - send_message(pos, "utake", stack) + send_message(pos, "utake", stack, index) check_empty(pos) minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos)) end