From 0defbdb5226391ece404cd9313fc44f6fc2c10d9 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 25 Feb 2017 06:59:43 -0700 Subject: [PATCH] add api support for registering groups --- api.txt | 30 +++++++++++++++- init.lua | 102 ++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 111 insertions(+), 21 deletions(-) diff --git a/api.txt b/api.txt index e6974c2..061ef44 100644 --- a/api.txt +++ b/api.txt @@ -30,7 +30,35 @@ hopper:add_container({ {"side", "default:furnace", "fuel"}, -- replenish furnace fuel from hopper at side }) +You can also register hopper interaction targets by group, or by a group and a specific group +value. For example: -We already have support for the wine barrel inside of the Wine mod and protected +hopper:add_container({ + {"top", "group:loot_chest", "loot"}, + {"bottom", "group:loot_chest", "loot"}, + {"side", "group:loot_chest", "loot"}, +}) + +Would cause hoppers to interact with the "loot" inventory of all nodes belonging to the group +"loot_chest", and + +hopper:add_container({ + {"top", "group:protected_container=1", "main"}, + {"bottom", "group:protected_container=1", "main"}, + {"side", "group:protected_container=1", "main"}, +}) + +Would cause hoppers to interact with the "main" inventory of nodes belonging to the group +"protected_container" provided they had a value of 1 in that group. Hoppers prioritize the most +specific definition first; they check for registrations for a specific node name, then +for registrations that apply to a node's group and group value, and then for registrations +applying to a node's group in general. + +Note that if multiple group registrations apply to the same node it's undefined which group +will take priority. Using the above examples, if there were a node that belonged to *both* +the groups "loot_chest" and "protected_container=1" there's no way of knowing ahead of time +whether hoppers would interact with the "loot" or "main" inventories. Try to avoid this situation. + +The hopper mod already have support for the wine barrel inside of the Wine mod and protected chests inside of Protector Redo, as well as default chests, furnaces and hoppers themselves. diff --git a/init.lua b/init.lua index 1f00d37..f373508 100644 --- a/init.lua +++ b/init.lua @@ -20,29 +20,63 @@ end -- API local containers = {} +local groups = {} local neighbors = {} -- global function to add new containers function hopper:add_container(list) for _, entry in pairs(list) do - local node_info = containers[entry[2]] - if node_info == nil then - node_info = {} - end - node_info[entry[1]] = entry[3] - containers[entry[2]] = node_info + + local target_node = entry[2] + local neighbor_node - -- result is a table of the form containers[target_node_name][relative_position][inventory_name] + if string.sub(target_node, 1, 6) == "group:" then + local group_identifier, group_number + local equals_index = string.find(target_node, "=") + if equals_index ~= nil then + group_identifier = string.sub(target_node, 7, equals_index-1) + -- it's possible that the string was of the form "group:blah = 1", in which case we want to trim spaces off the end of the group identifier + local space_index = string.find(group_identifier, " ") + if space_index ~= nil then + group_identifier = string.sub(group_identifier, 1, space_index-1) + end + group_number = tonumber(string.sub(target_node, equals_index+1, -1)) + else + group_identifier = string.sub(target_node, 7, -1) + group_number = "all" -- special value to indicate no number was provided + end + + local group_info = groups[group_identifier] + if group_info == nil then + group_info = {} + end + if group_info[group_number] == nil then + group_info[group_number] = {} + end + group_info[group_number][entry[1]] = entry[3] + groups[group_identifier] = group_info + neighbor_node = "group:"..group_identifier + -- result is a table of the form groups[group_identifier][group_number][relative_position][inventory_name] + else + local node_info = containers[target_node] + if node_info == nil then + node_info = {} + end + node_info[entry[1]] = entry[3] + containers[target_node] = node_info + neighbor_node = target_node + -- result is a table of the form containers[target_node_name][relative_position][inventory_name] + end local already_in_neighbors = false for _, value in pairs(neighbors) do - if value == entry[2] then + if value == neighbor_node then already_in_neighbors = true break end end if not already_in_neighbors then - table.insert(neighbors, entry[2]) + table.insert(neighbors, neighbor_node) end end end @@ -108,7 +142,32 @@ end hopper_usage = hopper_usage .. S("When used with furnaces, hoppers inject items into the furnace's \"raw material\" inventory slot when the narrow end is attached to the top or bottom and inject items into the furnace's \"fuel\" inventory slot when attached to the furnace's side.\n\nItems that cannot be placed in a target block's inventory will remain in the hopper.\n\nHoppers have the same permissions as the player that placed them. Hoppers placed by you are allowed to take items from or put items into locked chests that you own, but hoppers placed by other players will be unable to do so. A hopper's own inventory is not not owner-locked, though, so you can use this as a way to allow other players to deposit items into your locked chests.") local chute_long_desc = S("A chute to transfer items over longer distances.") -local chute_usage = S("Chutes operate much like hoppers but do not have their own intake capability. Items can only be inserted into a chute manually or by a hopper connected to a chute. They transfer items in the direction indicated by the arrow on their narrow segment at a rate of one item per second. They have a small buffer capacity, and any items that can't be placed into the target block's inventory will remain lodged in the chute's buffer until manuall removed or their destination becomes available.") +local chute_usage = S("Chutes operate much like hoppers but do not have their own intake capability. Items can only be inserted into a chute manually or by a hopper connected to a chute. They transfer items in the direction indicated by the arrow on their narrow segment at a rate of one item per second. They have a small buffer capacity, and any items that can't be placed into the target block's inventory will remain lodged in the chute's buffer until manually removed or their destination becomes available.") + +------------------------------------------------------------------------------------------- +-- Target inventory retrieval + +-- looks first for a registration matching the specific node name, then for a registration +-- matching group and value, then for a registration matching a group and *any* value +local get_registered_inventories_for = function(target_node_name) + local output = containers[target_node_name] + if output ~= nil then return output end + + local target_def = minetest.registered_nodes[target_node_name] + if target_def.groups == nil then return nil end + + for group, value in pairs(target_def.groups) do + local registered_group = groups[group] + if registered_group ~= nil then + output = registered_group[value] + if output ~= nil then return output end + output = registered_group["all"] + if output ~= nil then return output end + end + end + + return nil +end ------------------------------------------------------------------------------------------- -- Inventory transfer functions @@ -546,11 +605,12 @@ minetest.register_node("hopper:chute", { end local destination_node = minetest.get_node(destination_pos) - if containers[destination_node.name] ~= nil then + local registered_inventories = get_registered_inventories_for(destination_node.name) + if registered_inventories ~= nil then if output_direction == "horizontal" then - send_item_to(pos, destination_pos, destination_node, containers[destination_node.name]["side"]) + send_item_to(pos, destination_pos, destination_node, registered_inventories["side"]) else - send_item_to(pos, destination_pos, destination_node, containers[destination_node.name]["bottom"]) + send_item_to(pos, destination_pos, destination_node, registered_inventories["bottom"]) end else send_item_to(pos, destination_pos, destination_node) @@ -689,19 +749,21 @@ minetest.register_abm({ local source_node = minetest.get_node(source_pos) local destination_node = minetest.get_node(destination_pos) - - if containers[source_node.name] ~= nil then - take_item_from(pos, source_pos, source_node, containers[source_node.name]["top"]) + + local registered_source_inventories = get_registered_inventories_for(source_node.name) + if registered_source_inventories ~= nil then + take_item_from(pos, source_pos, source_node, registered_source_inventories["top"]) end - if containers[destination_node.name] ~= nil then + local registered_destination_inventories = get_registered_inventories_for(destination_node.name) + if registered_destination_inventories ~= nil then if output_direction == "horizontal" then - send_item_to(pos, destination_pos, destination_node, containers[destination_node.name]["side"]) + send_item_to(pos, destination_pos, destination_node, registered_destination_inventories["side"]) else - send_item_to(pos, destination_pos, destination_node, containers[destination_node.name]["bottom"]) + send_item_to(pos, destination_pos, destination_node, registered_destination_inventories["bottom"]) end else - send_item_to(pos, destination_pos, destination_node) + send_item_to(pos, destination_pos, destination_node) -- for handling ejection end end, })