diff --git a/mods/tools/hopper/.luacheckrc b/mods/tools/hopper/.luacheckrc deleted file mode 100644 index b185526c2..000000000 --- a/mods/tools/hopper/.luacheckrc +++ /dev/null @@ -1,22 +0,0 @@ -std = "lua51c" - -ignore = { - "21[23]", -- unused argument -} - -max_line_length = 250 - -read_globals = { - "default", - "ItemStack", - "lucky_block", - "minetest", - "screwdriver", - "vector", -} - -globals = {"hopper"} - -files["doc.lua"] = { - max_line_length = 9999, -} diff --git a/mods/tools/hopper/CHANGELOG.md b/mods/tools/hopper/CHANGELOG.md deleted file mode 100644 index 8aa0e611c..000000000 --- a/mods/tools/hopper/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -### Change log - -- 0.1 - Initial release from jordan4ibanez -- 0.2 - Fixed tool glitch (wear restored by accident) -- 0.3 - transfer function added -- 0.4 - Supports locked chest and protected chest -- 0.5 - Works with 0.4.13's new shift+click for newly placed Hoppers -- 0.6 - Remove formspec from hopper nodes to improve speed for servers -- 0.7 - Halved hopper capacity, can be dug by wooden pick -- 0.8 - Added Napiophelios' new textures and tweaked code -- 0.9 - Added support for Wine mod's wine barrels -- 1.0 - New furances do not work properly with hoppers so old reverted to abm furnaces -- 1.1 - Hoppers now work with new node timer Furnaces. Reduced Abm's and tidied code. -- 1.2 - Added simple API so that hoppers can work with other containers. -- 1.3 - Hoppers now call on_metadata_inventory_put and on_metadata_inventory_take, triggering furnace timers via their standard callbacks. Updated side hopper rotation handling to allow it to function in any orientation. Added settings options to use 16-pixel or 32-pixel textures. Added settings option to allow explicit crafting of standard/side hoppers or to allow crafting of a single item that selects which type to use on place. Added in-game documentation via optional "doc" mod dependency -- 1.4 - Added intllib support -- 1.5 - Added chutes -- 1.6 - Added "eject items" button to formspecs, "group" support to the API -- 1.7 - Added sorter block to allow for more sophisticated item transfer arrangements -- master - Maintained by minetest-mods and contributors. Refer to git history for details. - diff --git a/mods/tools/hopper/README.md b/mods/tools/hopper/README.md index 5bb75184b..6bada393d 100644 --- a/mods/tools/hopper/README.md +++ b/mods/tools/hopper/README.md @@ -4,56 +4,42 @@ Based on jordan4ibanez's original hoppers mod, optimized by TenPlus1 and FaceDeer, with chutes and sorters by FaceDeer -Lucky Blocks: 2 - -Change log: [CHANGELOG.md](CHANGELOG.md) - -### Functionality description - -#### Hopper node - -Hoppers are nodes that can transfer items to and from the inventories of adjacent nodes. The transfer direction depends on the orientation of the hopper. - -The wide end of a hopper is its "input" end, if there is a compatible container (e.g. a chest), one item per second is taken into its own internal inventory. The hopper will also draw in dropped items. - -The narrow end of the hopper is its "output" end. It can be either straight or 90° bent, relative to the input. It will attempt to inject items into a compatible container connected to it. On failure, the item is either held back in the hopper inventory or ejected, depending on its configuration. - -#### Chute node +Hoppers are nodes that can transfer items to and from the inventories of adjacent nodes. The wide end of a hopper is its "input" end, if there's a chest or other compatible container it will take one item per second into its own internal inventory. It will also draw in items that have been dropped here. The narrow end of the hopper is its "output" end. It will attempt to inject items into a compatible container located at its output end. If there's no compatible container and the hopper's "eject" mode has been enabled it will instead dump the items out into the world. -The "chute" node acts as a pipe for items injected by a hopper or sorter. Use a screwdriver tool to achieve the desired rotation. +The location of a hopper relative to a furnace determines which inventory slots the hopper will affect. A hopper directly above a furnace will inject items into the furnace's input slot. A hopper to the furnace's side will inject items into the furnace's fuel slot. A hopper below the furnace will pull items out of the furnace's output slot. Hoppers cannot inject items into inappropriate slots; non-fuel items will not be placed into the furnace's fuel slot and non-cookable items will not be placed into the input slot. -#### Sorter node +Also included in this mod are a "chute" node and a "sorter" node. These allow for items to be routed over short distances and distributed in sophisticated ways. -Item stacks placed into the "filter" grid of the sorter node inventory define how to distribute the incoming items into the two outputs. +A chute simply moves items into adjacent nodes. You'll need to use a hopper or sorter to inject items into it to move along. A screwdriver is a useful tool for getting chutes aimed correctly. - * Matching items are sent into the direction of the large arrow (`V`). - * Other items are sent to the 90° side output. - -Special case: The "filter all" option will cause the sorter to attempt to send *all* items in the direction of the large arrow. On failure, they're sent in the direction of the smaller arrow. Use-cases: - - * Protection against overflowing containers - * Protection against unaccepted items in the destination slot(s), such as the furnace fuel slot - -#### Built-in mod compatibility - -The following nodes are supported out-of-the-box. "above"/"below"/"side" describe the location of the hopper. - - * `default:furnace` - * Above: routed to the input slot - * Below: routed to the output slots - * Sides: routed to the fuel slot - * `default:chest(_locked)`, `protector:chest` - * All sides: routed to the main slot - * `wine:wine_barrel` - * Above: routed to the destination slots - * Below: routed to the source slot - * Sides: routed to the source slot +A sorter has two different outputs. Its inventory has a "filter" grid; place items in this grid to set the sorter's filter. Items that match the filter will be sent out the output with the large arrow and other items will be sent out the output with the smaller arrow. A "filter all" option will cause the sorter to attempt to send *all* items in the direction of the large arrow and then if that fails send them in the direction of the smaller arrow. This allows you to have an "overflow" storage should the sorter's primary target fill up, or when used in combination with a selective container (like the furnace's fuel slot, for example) it allows the target inventory to do the filtering for you. ### Advanced settings -This mod has several configurable settings. See settings menu or [settingtypes.txt](settingtypes.txt) for details. - -* Hopper texture size: 16x16 pixels (default) or 32x32 pixels -* Single craftable item: output is straight or rotated by 90° to the side based on how you place it (default). When disabled, straight and bent hoppers must be crafted separately. -* Eject items button: option to remove the "eject items" button from hoppers +This mod has several configurable settings found in the advanced settings menu. + +* Hopper texture size: can be set to 16 pixels (matching most standard Minetest node textures) or 32 pixels +* Single craftable item: When enabled (the default) hoppers are crafted as a single item and then select whether the output has a 90-degree turn to the side based on how you place it. When disabled you can craft straight and bent hoppers as separate items. +* Eject items button: this can be used to remove the "eject items" button from hoppers, if it is not desired. + +### Change log + +- 0.1 - Initial release from jordan4ibanez +- 0.2 - Fixed tool glitch (wear restored by accident) +- 0.3 - transfer function added +- 0.4 - Supports locked chest and protected chest +- 0.5 - Works with 0.4.13's new shift+click for newly placed Hoppers +- 0.6 - Remove formspec from hopper nodes to improve speed for servers +- 0.7 - Halved hopper capacity, can be dug by wooden pick +- 0.8 - Added Napiophelios' new textures and tweaked code +- 0.9 - Added support for Wine mod's wine barrels +- 1.0 - New furances do not work properly with hoppers so old reverted to abm furnaces +- 1.1 - Hoppers now work with new node timer Furnaces. Reduced Abm's and tidied code. +- 1.2 - Added simple API so that hoppers can work with other containers. +- 1.3 - Hoppers now call on_metadata_inventory_put and on_metadata_inventory_take, triggering furnace timers via their standard callbacks. Updated side hopper rotation handling to allow it to function in any orientation. Added settings options to use 16-pixel or 32-pixel textures. Added settings option to allow explicit crafting of standard/side hoppers or to allow crafting of a single item that selects which type to use on place. Added in-game documentation via optional "doc" mod dependency +- 1.4 - Added intllib support +- 1.5 - Added chutes +- 1.6 - Added "eject items" button to formspecs, "group" support to the API +- 1.7 - Added sorter block to allow for more sophisticated item transfer arrangements +Lucky Blocks: 2 diff --git a/mods/tools/hopper/abms.lua b/mods/tools/hopper/abms.lua index 3eaf0d219..edef8ed4a 100644 --- a/mods/tools/hopper/abms.lua +++ b/mods/tools/hopper/abms.lua @@ -5,32 +5,32 @@ minetest.register_abm({ nodenames = {"hopper:hopper", "hopper:hopper_side"}, interval = 1.0, chance = 1, - action = function(pos, _, _, active_object_count_wider) + action = function(pos, node, active_object_count, active_object_count_wider) if active_object_count_wider == 0 then return end - + local inv = minetest.get_meta(pos):get_inventory() - if not inv then - return - end + local posob for _,object in pairs(minetest.get_objects_inside_radius(pos, 1)) do - local entity = not object:is_player() and object:get_luaentity() - - if entity - and entity.name == "__builtin:item" - and inv:room_for_item("main", ItemStack(entity.itemstring)) then + if not object:is_player() + and object:get_luaentity() + and object:get_luaentity().name == "__builtin:item" + and inv + and inv:room_for_item("main", + ItemStack(object:get_luaentity().itemstring)) then - local posob = object:get_pos() + posob = object:getpos() if math.abs(posob.x - pos.x) <= 0.5 and posob.y - pos.y <= 0.85 and posob.y - pos.y >= 0.3 then - inv:add_item("main", ItemStack(entity.itemstring)) + inv:add_item("main", + ItemStack(object:get_luaentity().itemstring)) - entity.itemstring = "" + object:get_luaentity().itemstring = "" object:remove() end end @@ -85,7 +85,7 @@ minetest.register_abm({ chance = 1, catch_up = false, - action = function(pos, node, _, _) + action = function(pos, node, active_object_count, active_object_count_wider) local source_pos, destination_pos, destination_dir if node.name == "hopper:hopper_side" then source_pos = vector.add(pos, directions[node.param2].src) @@ -96,21 +96,21 @@ minetest.register_abm({ source_pos = vector.subtract(pos, destination_dir) destination_pos = vector.add(pos, destination_dir) end - + local output_direction if destination_dir.y == 0 then output_direction = "horizontal" end - + local source_node = minetest.get_node(source_pos) local destination_node = minetest.get_node(destination_pos) - local registered_source_inventories = hopper.get_registered(source_node.name) + local registered_source_inventories = hopper.get_registered_inventories_for(source_node.name) if registered_source_inventories ~= nil then hopper.take_item_from(pos, source_pos, source_node, registered_source_inventories["top"]) end - - local registered_destination_inventories = hopper.get_registered(destination_node.name) + + local registered_destination_inventories = hopper.get_registered_inventories_for(destination_node.name) if registered_destination_inventories ~= nil then if output_direction == "horizontal" then hopper.send_item_to(pos, destination_pos, destination_node, registered_destination_inventories["side"]) diff --git a/mods/tools/hopper/api.lua b/mods/tools/hopper/api.lua index 347ec5112..4eb62821c 100644 --- a/mods/tools/hopper/api.lua +++ b/mods/tools/hopper/api.lua @@ -2,107 +2,60 @@ hopper.containers = {} hopper.groups = {} hopper.neighbors = {} -local function parse_group(target_node) - local number - local identifier - - local equals_index = string.find(target_node, "=") - if equals_index ~= nil then - 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(identifier, " ") - if space_index ~= nil then - identifier = string.sub(identifier, 1, space_index-1) - end - number = tonumber(string.sub(target_node, equals_index+1, -1)) - else - identifier = string.sub(target_node, 7, -1) - number = "all" -- special value to indicate no number was provided - end - - return identifier, number -end - -local function is_already_in_neighbors(neighbor_node) - for _, value in pairs(hopper.neighbors) do - if value == neighbor_node then - return true - end - end - return false -end - -- global function to add new containers function hopper:add_container(list) for _, entry in pairs(list) do - - local relative_position = entry[1] + local target_node = entry[2] - - local inventory_info = entry - inventory_info.inventory_name = entry[3] - table.remove(inventory_info, 1) - table.remove(inventory_info, 1) - table.remove(inventory_info, 1) - local neighbor_node + if string.sub(target_node, 1, 6) == "group:" then - local group_identifier, group_number = parse_group(target_node) - - if hopper.groups[group_identifier] == nil then - hopper.groups[group_identifier] = {} + 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 - if hopper.groups[group_identifier][group_number] == nil then - hopper.groups[group_identifier][group_number] = {} + + local group_info = hopper.groups[group_identifier] + if group_info == nil then + group_info = {} end - if hopper.groups[group_identifier][group_number].extra == nil then - hopper.groups[group_identifier][group_number].extra = {} + if group_info[group_number] == nil then + group_info[group_number] = {} end - - hopper.groups[group_identifier][group_number][relative_position] = inventory_info + group_info[group_number][entry[1]] = entry[3] + hopper.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 - if hopper.containers[target_node] == nil then - hopper.containers[target_node] = {} - end - if hopper.containers[target_node].extra == nil then - hopper.containers[target_node].extra = {} + local node_info = hopper.containers[target_node] + if node_info == nil then + node_info = {} end - - hopper.containers[target_node][relative_position] = inventory_info + node_info[entry[1]] = entry[3] + hopper.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 - - if not is_already_in_neighbors(neighbor_node) then - table.insert(hopper.neighbors, neighbor_node) - end - end -end - --- global function for additional information about containers -function hopper:set_extra_container_info(list) - for _, entry in pairs(list) do - local target_node = entry[1] - table.remove(entry, 1) -- only extra information - if string.sub(target_node, 1, 6) == "group:" then - local group_identifier, group_number = parse_group(target_node) - if not is_already_in_neighbors("group:" .. group_identifier) then - minetest.log("error","An attempt to add extra information for " .. - target_node .. " in the absence of the main one") + + local already_in_neighbors = false + for _, value in pairs(hopper.neighbors) do + if value == neighbor_node then + already_in_neighbors = true break end - hopper.groups[group_identifier][group_number].extra = entry - -- result is a table of the form: - -- groups[group_identifier][group_number]["extra"][list of extra information] - else - if not is_already_in_neighbors(target_node) then - minetest.log("error","An attempt to add extra information for " .. - target_node .. " in the absence of the main one") - break - end - hopper.containers[target_node].extra = entry - -- result is a table of the form: - -- containers[target_node_name]["extra"][list of extra information] + end + if not already_in_neighbors then + table.insert(hopper.neighbors, neighbor_node) end end end @@ -116,10 +69,10 @@ hopper:add_container({ {"bottom", "hopper:hopper", "main"}, {"side", "hopper:hopper", "main"}, {"side", "hopper:hopper_side", "main"}, - + {"bottom", "hopper:chute", "main"}, {"side", "hopper:chute", "main"}, - + {"bottom", "hopper:sorter", "main"}, {"side", "hopper:sorter", "main"}, }) @@ -129,15 +82,15 @@ if minetest.get_modpath("default") then {"top", "default:chest", "main"}, {"bottom", "default:chest", "main"}, {"side", "default:chest", "main"}, - + {"top", "default:furnace", "dst"}, {"bottom", "default:furnace", "src"}, {"side", "default:furnace", "fuel"}, - + {"top", "default:furnace_active", "dst"}, {"bottom", "default:furnace_active", "src"}, {"side", "default:furnace_active", "fuel"}, - + {"top", "default:chest_locked", "main"}, {"bottom", "default:chest_locked", "main"}, {"side", "default:chest_locked", "main"}, diff --git a/mods/tools/hopper/api.md b/mods/tools/hopper/api.txt similarity index 59% rename from mods/tools/hopper/api.md rename to mods/tools/hopper/api.txt index a9303edc0..061ef44e1 100644 --- a/mods/tools/hopper/api.md +++ b/mods/tools/hopper/api.txt @@ -1,13 +1,17 @@ -# Hopper API +Hopper API +---------- -This API provides two functions for adding containers such as chests and furnaces that will interact with hoppers. +This API is kept simple by adding a single command which allows mods to add +containers like chests and furnaces to the hopper check list. -## hopper:add_container -```lua -hopper:add_container({ {"where_from", "node_name", "inventory_name"}, }) -``` +Command Usage +------------- + +Make sure any mods using this function has 'hopper' in the depends.txt file. + +hopper:add_container({ {"where_from", "node_name", "inventory_name"} }) 'where_from' is a string telling the api that items are coming from either the 'top' node into a hopper below, going into the 'bottom' node @@ -20,35 +24,29 @@ hopper:add_container({ {"where_from", "node_name", "inventory_name"}, }) e.g. -```lua hopper:add_container({ {"top", "default:furnace", "dst"}, -- take cooked items from above into hopper below {"bottom", "default:furnace", "src"}, -- insert items below to be cooked from hopper above {"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: -```lua 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 -```lua 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 @@ -61,36 +59,6 @@ will take priority. Using the above examples, if there were a node that belonged 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. -### Optional parameter: get_inventory function - -```lua -hopper:add_container({ {"where_from", "node_name", "inventory_name", get_inventory = function get_inventory(node_pos)}, }) -``` - -This option allows you to use a different inventory instead of the inventory of this node. Unlike others, this parameter does not have to be set. - -## hopper:set_extra_container_info - -```lua -hopper:set_extra_container_info({ {"node_name", EXTRA PARAMETERS}, }) -``` - -The function adds additional information about the container, which does not depend on where the items arrive. Before using it, you need to add a container. -Of course, it is not necessary to use it. - -### EXTRA PARAMETER: set_hopper_param2 function - -```lua -hopper:set_extra_container_info({ {"node_name", set_hopper_param2 = function set_hopper_param2(hopper_pos, node_pos)}, }) -``` - -When we place the hopper, we have the position of the node we are looking at and the position of the funnel. -When other nodes get into the nodebox of one node, the information becomes insufficient, see [minetest 9147 issue](https://github.com/minetest/minetest/issues/9147). In particular, this feature was useful for adding [connected chests](https://github.com/HybridDog/connected_chests/pull/14) support. - -This function takes priority over the usual installation of the hopper. - -## Already supported containers - 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/mods/tools/hopper/crafts.lua b/mods/tools/hopper/crafts.lua index f6bf6b8e3..065c04f87 100644 --- a/mods/tools/hopper/crafts.lua +++ b/mods/tools/hopper/crafts.lua @@ -7,14 +7,14 @@ if minetest.get_modpath("default") then {"","default:steel_ingot",""}, } }) - + minetest.register_craft({ output = "hopper:chute", recipe = { {"default:steel_ingot","default:chest","default:steel_ingot"}, } }) - + minetest.register_craft({ output = "hopper:sorter", recipe = { @@ -23,7 +23,7 @@ if minetest.get_modpath("default") then {"","default:steel_ingot",""}, } }) - + if not hopper.config.single_craftable_item then minetest.register_craft({ output = "hopper:hopper_side", @@ -32,13 +32,13 @@ if minetest.get_modpath("default") then {"","","default:steel_ingot"}, } }) - + minetest.register_craft({ output = "hopper:hopper_side", type="shapeless", recipe = {"hopper:hopper"}, }) - + minetest.register_craft({ output = "hopper:hopper", type="shapeless", diff --git a/mods/tools/hopper/init.lua b/mods/tools/hopper/init.lua index 7f2209840..2148fa0d6 100644 --- a/mods/tools/hopper/init.lua +++ b/mods/tools/hopper/init.lua @@ -30,20 +30,25 @@ dofile(MP.."/abms.lua") -- Formspec handling minetest.register_on_player_receive_fields(function(player, formname, fields) - if string.sub(formname, 1, 16) ~= "hopper_formspec:" then - return - end - - local pos = minetest.string_to_pos(string.sub(formname, 17, -1)) - local meta = minetest.get_meta(pos) - if fields.eject then + if "hopper_formspec:" == string.sub(formname, 1, 16) then + local pos = minetest.string_to_pos(string.sub(formname, 17, -1)) + local meta = minetest.get_meta(pos) local eject_setting = meta:get_string("eject") == "true" - -- "" deletes the key - meta:set_string("eject", eject_setting and "" or "true") - end - if fields.filter_all then local filter_all_setting = meta:get_string("filter_all") == "true" - meta:set_string("filter_all", filter_all_setting and "" or "true") + if fields.eject then + if eject_setting then + meta:set_string("eject", nil) + else + meta:set_string("eject", "true") + end + end + if fields.filter_all then + if filter_all_setting then + meta:set_string("filter_all", nil) + else + meta:set_string("filter_all", "true") + end + end end end) diff --git a/mods/tools/hopper/nodes/chute.lua b/mods/tools/hopper/nodes/chute.lua index 1d0ca714c..48d6b2d7f 100644 --- a/mods/tools/hopper/nodes/chute.lua +++ b/mods/tools/hopper/nodes/chute.lua @@ -41,14 +41,17 @@ minetest.register_node("hopper:chute", { {-0.2, -0.2, 0.3, 0.2, 0.2, 0.7}, }, }, - + on_construct = function(pos) local inv = minetest.get_meta(pos):get_inventory() inv:set_size("main", 2*2) end, on_place = function(itemstack, placer, pointed_thing, node_name) + local pos = pointed_thing.under local pos2 = pointed_thing.above + local x = pos.x - pos2.x + local z = pos.z - pos2.z local returned_stack, success = minetest.item_place_node(itemstack, placer, pointed_thing) if success then @@ -57,7 +60,7 @@ minetest.register_node("hopper:chute", { end return returned_stack end, - + can_dig = function(pos,player) local inv = minetest.get_meta(pos):get_inventory() return inv:is_empty("main") @@ -78,7 +81,7 @@ minetest.register_node("hopper:chute", { local timer = minetest.get_node_timer(pos) if not timer:is_started() then timer:start(1) - end + end end, on_timer = function(pos, elapsed) @@ -92,9 +95,9 @@ minetest.register_node("hopper:chute", { if dir.y == 0 then output_direction = "horizontal" end - + local destination_node = minetest.get_node(destination_pos) - local registered_inventories = hopper.get_registered(destination_node.name) + local registered_inventories = hopper.get_registered_inventories_for(destination_node.name) if registered_inventories ~= nil then if output_direction == "horizontal" then hopper.send_item_to(pos, destination_pos, destination_node, registered_inventories["side"]) @@ -104,7 +107,7 @@ minetest.register_node("hopper:chute", { else hopper.send_item_to(pos, destination_pos, destination_node) end - + if not inv:is_empty("main") then minetest.get_node_timer(pos):start(1) end diff --git a/mods/tools/hopper/nodes/hoppers.lua b/mods/tools/hopper/nodes/hoppers.lua index 81f2ea343..d1e6456ca 100644 --- a/mods/tools/hopper/nodes/hoppers.lua +++ b/mods/tools/hopper/nodes/hoppers.lua @@ -18,45 +18,33 @@ local function get_hopper_formspec(pos) return formspec end --- unfortunately param2 overrides are needed for side hoppers even in the non-single-craftable-item case --- because they are literally *side* hoppers - their spouts point to the side rather than to the front, so --- the default item_place_node orientation code will not orient them pointing toward the selected surface. local hopper_on_place = function(itemstack, placer, pointed_thing, node_name) - local returned_stack, success, _ - - if not (hopper.config.single_craftable_item or node_name == "hopper:hopper_side") then - returned_stack, _ = minetest.item_place_node(itemstack, placer, pointed_thing) - return returned_stack - end - - local pointed_pos = pointed_thing.under - local hopper_pos = pointed_thing.above - - local param2 - local pointed_registered = hopper.get_registered(minetest.get_node(pointed_pos).name) - if pointed_registered and pointed_registered.extra.set_hopper_param2 then - param2 = pointed_registered.extra.set_hopper_param2(hopper_pos, pointed_pos) - else - local param2_by_offset = { - [vector.new(-1, 0, 0):to_string()] = 0, - [vector.new( 0, 0, 1):to_string()] = 1, - [vector.new( 1, 0, 0):to_string()] = 2, - [vector.new( 0, 0,-1):to_string()] = 3, - } - param2 = param2_by_offset[(pointed_pos - hopper_pos):to_string()] - end - - if param2 then - _, success = minetest.item_place_node(ItemStack("hopper:hopper_side"), placer, pointed_thing, param2) + local pos = pointed_thing.under + local pos2 = pointed_thing.above + local x = pos.x - pos2.x + local z = pos.z - pos2.z + + local returned_stack, success + -- unfortunately param2 overrides are needed for side hoppers even in the non-single-craftable-item case + -- because they are literally *side* hoppers - their spouts point to the side rather than to the front, so + -- the default item_place_node orientation code will not orient them pointing toward the selected surface. + if x == -1 and (hopper.config.single_craftable_item or node_name == "hopper:hopper_side") then + returned_stack, success = minetest.item_place_node(ItemStack("hopper:hopper_side"), placer, pointed_thing, 0) + elseif x == 1 and (hopper.config.single_craftable_item or node_name == "hopper:hopper_side") then + returned_stack, success = minetest.item_place_node(ItemStack("hopper:hopper_side"), placer, pointed_thing, 2) + elseif z == -1 and (hopper.config.single_craftable_item or node_name == "hopper:hopper_side") then + returned_stack, success = minetest.item_place_node(ItemStack("hopper:hopper_side"), placer, pointed_thing, 3) + elseif z == 1 and (hopper.config.single_craftable_item or node_name == "hopper:hopper_side") then + returned_stack, success = minetest.item_place_node(ItemStack("hopper:hopper_side"), placer, pointed_thing, 1) else if hopper.config.single_craftable_item then node_name = "hopper:hopper" -- For cases where single_craftable_item was set on an existing world and there are still side hoppers in player inventories end - _, success = minetest.item_place_node(ItemStack(node_name), placer, pointed_thing) + returned_stack, success = minetest.item_place_node(ItemStack(node_name), placer, pointed_thing) end - + if success then - local meta = minetest.get_meta(hopper_pos) + local meta = minetest.get_meta(pos2) meta:set_string("placer", placer:get_player_name()) if not minetest.settings:get_bool("creative_mode") then itemstack:take_item() @@ -202,7 +190,7 @@ minetest.register_node("hopper:hopper_side", { {-0.7, -0.3, -0.15, 0.15, 0.0, 0.15}, }, }, - + on_construct = function(pos) local inv = minetest.get_meta(pos):get_inventory() inv:set_size("main", 4*4) @@ -211,7 +199,7 @@ minetest.register_node("hopper:hopper_side", { on_place = function(itemstack, placer, pointed_thing) return hopper_on_place(itemstack, placer, pointed_thing, "hopper:hopper_side") end, - + can_dig = function(pos,player) local inv = minetest.get_meta(pos):get_inventory() return inv:is_empty("main") diff --git a/mods/tools/hopper/nodes/sorter.lua b/mods/tools/hopper/nodes/sorter.lua index 0c8c54a01..e0dc12bba 100644 --- a/mods/tools/hopper/nodes/sorter.lua +++ b/mods/tools/hopper/nodes/sorter.lua @@ -16,7 +16,7 @@ end local function get_sorter_formspec(pos) local spos = hopper.get_string_pos(pos) - + local filter_all = minetest.get_meta(pos):get_string("filter_all") == "true" local y_displace = 0 local filter_texture, filter_button_tooltip, filter_body @@ -30,11 +30,11 @@ local function get_sorter_formspec(pos) filter_button_tooltip = FS("This sorter is currently set to only send items listed\nin the filter list in the direction of the arrow.\nClick this button to set it to try sending all\nitems that way first.") y_displace = 1.6 end - + local formspec = "size[8," .. 7 + y_displace .. "]" .. hopper.formspec_bg - .. filter_body + .. filter_body .. "list[nodemeta:" .. spos .. ";main;3,".. tostring(0.3 + y_displace) .. ";2,2;]" .. ("image_button_exit[0,%g;1,1;%s;filter_all;]"):format(y_displace, filter_texture) .. "tooltip[filter_all;" .. filter_button_tooltip.. "]" @@ -72,16 +72,19 @@ minetest.register_node("hopper:sorter", { {-0.2, -0.3, -0.2, 0.2, -0.7, 0.2}, }, }, - + on_construct = function(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() inv:set_size("main", 2*2) inv:set_size("filter", 8) end, - + on_place = function(itemstack, placer, pointed_thing, node_name) + local pos = pointed_thing.under local pos2 = pointed_thing.above + local x = pos.x - pos2.x + local z = pos.z - pos2.z local returned_stack, success = minetest.item_place_node(itemstack, placer, pointed_thing) if success then @@ -90,13 +93,13 @@ minetest.register_node("hopper:sorter", { end return returned_stack end, - + can_dig = function(pos,player) local meta = minetest.get_meta(pos); local inv = meta:get_inventory() return inv:is_empty("main") end, - + on_rightclick = function(pos, node, clicker, itemstack) if minetest.is_protected(pos, clicker:get_player_name()) and not minetest.check_player_privs(clicker, "protection_bypass") then return @@ -104,7 +107,7 @@ minetest.register_node("hopper:sorter", { minetest.show_formspec(clicker:get_player_name(), "hopper_formspec:"..minetest.pos_to_string(pos), get_sorter_formspec(pos)) end, - + allow_metadata_inventory_put = function(pos, listname, index, stack, player) if listname == "filter" then local inv = minetest.get_inventory({type="node", pos=pos}) @@ -113,7 +116,7 @@ minetest.register_node("hopper:sorter", { end return stack:get_count() end, - + allow_metadata_inventory_take = function(pos, listname, index, stack, player) if listname == "filter" then local inv = minetest.get_inventory({type="node", pos=pos}) @@ -122,7 +125,7 @@ minetest.register_node("hopper:sorter", { end return stack:get_count() end, - + allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) if to_list == "filter" then local inv = minetest.get_inventory({type="node", pos=pos}) @@ -132,11 +135,11 @@ minetest.register_node("hopper:sorter", { elseif from_list == "filter" then local inv = minetest.get_inventory({type="node", pos=pos}) inv:set_stack(from_list, from_index, ItemStack("")) - return 0 + return 0 end return count end, - + on_metadata_inventory_put = function(pos, listname, index, stack, player) hopper.log_inventory(("%s moves stuff to sorter at %s"):format( player:get_player_name(), minetest.pos_to_string(pos))) @@ -144,15 +147,15 @@ minetest.register_node("hopper:sorter", { local timer = minetest.get_node_timer(pos) if not timer:is_started() then timer:start(1) - end + end end, - + on_timer = function(pos, elapsed) local meta = minetest.get_meta(pos); local inv = meta:get_inventory() -- build a filter list - local filter_items = nil + local filter_items = nil if meta:get_string("filter_all") ~= "true" then filter_items = {} local filter_inv_size = inv:get_size("filter") @@ -164,34 +167,50 @@ minetest.register_node("hopper:sorter", { end end end - + local node = minetest.get_node(pos) local dir = minetest.facedir_to_dir(node.param2) local default_destination_pos = vector.add(pos, dir) - local default_output_direction = (dir.y == 0) and "side" or "bottom" + local default_output_direction + if dir.y == 0 then + default_output_direction = "horizontal" + end dir = bottomdir(node.param2) local filter_destination_pos = vector.add(pos, dir) - local filter_output_direction = (dir.y == 0) and "side" or "bottom" - - --- returns success? = true/false - local function try_send_item(output_dir, dst_pos) - local dst_node = minetest.get_node(dst_pos) - local registered_inventories = hopper.get_registered(dst_node.name) + local filter_output_direction + if dir.y == 0 then + filter_output_direction = "horizontal" + end - if registered_inventories ~= nil then - return hopper.send_item_to(pos, dst_pos, dst_node, registered_inventories[output_dir], filter_items) + local success = false + + local filter_destination_node = minetest.get_node(filter_destination_pos) + local registered_inventories = hopper.get_registered_inventories_for(filter_destination_node.name) + if registered_inventories ~= nil then + if filter_output_direction == "horizontal" then + success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, registered_inventories["side"], filter_items) + else + success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, registered_inventories["bottom"], filter_items) end - - return hopper.send_item_to(pos, dst_pos, dst_node, nil, filter_items) + else + success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, nil, filter_items) end - - if not try_send_item(filter_output_direction, filter_destination_pos) then - -- weren't able to put something in the filter destination, for whatever reason. - -- Now we can start moving stuff forward to the default. - try_send_item(default_output_direction, default_destination_pos) + + if not success then -- weren't able to put something in the filter destination, for whatever reason. Now we can start moving stuff forward to the default. + local default_destination_node = minetest.get_node(default_destination_pos) + local registered_inventories = hopper.get_registered_inventories_for(default_destination_node.name) + if registered_inventories ~= nil then + if default_output_direction == "horizontal" then + hopper.send_item_to(pos, default_destination_pos, default_destination_node, registered_inventories["side"]) + else + hopper.send_item_to(pos, default_destination_pos, default_destination_node, registered_inventories["bottom"]) + end + else + hopper.send_item_to(pos, default_destination_pos, default_destination_node) + end end - + if not inv:is_empty("main") then minetest.get_node_timer(pos):start(1) end diff --git a/mods/tools/hopper/utility.lua b/mods/tools/hopper/utility.lua index c99dd730d..0e8c6461e 100644 --- a/mods/tools/hopper/utility.lua +++ b/mods/tools/hopper/utility.lua @@ -1,9 +1,10 @@ +local S = minetest.get_translator("hopper") local FS = hopper.translator_escaped -- 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 -hopper.get_registered = function(target_node_name) +hopper.get_registered_inventories_for = function(target_node_name) local output = hopper.containers[target_node_name] if output ~= nil then return output end @@ -68,58 +69,39 @@ local get_placer = function(player_name) return nil end -local function get_container_inventory(node_pos, inv_info) - local get_inventory_fn = inv_info.get_inventory - - local inventory - if get_inventory_fn then - inventory = get_inventory_fn(node_pos) - if not inventory then - local target_node = minetest.get_node(node_pos) - minetest.log("error","No inventory from api get_inventory function: " .. - target_node.name .. " on " .. vector.to_string(node_pos)) - end - else - inventory = minetest.get_meta(node_pos):get_inventory() - end - return inventory -end - -- Used to remove items from the target block and put it into the hopper's inventory -hopper.take_item_from = function(hopper_pos, target_pos, target_node, target_inv_info) +hopper.take_item_from = function(hopper_pos, target_pos, target_node, target_inventory_name) + if target_inventory_name == nil then + return + end local target_def = minetest.registered_nodes[target_node.name] if not target_def then return end --hopper inventory - local hopper_meta = minetest.get_meta(hopper_pos) + local hopper_meta = minetest.get_meta(hopper_pos); local hopper_inv = hopper_meta:get_inventory() local placer = get_placer(hopper_meta:get_string("placer")) --source inventory - local target_inv_name = target_inv_info.inventory_name - local target_inv = get_container_inventory(target_pos, target_inv_info) - if not target_inv then - return false - end - - local target_inv_size = target_inv:get_size(target_inv_name) - if target_inv:is_empty(target_inv_name) == false then + local target_inv = minetest.get_meta(target_pos):get_inventory() + local target_inv_size = target_inv:get_size(target_inventory_name) + if target_inv:is_empty(target_inventory_name) == false then for i = 1,target_inv_size do - local stack = target_inv:get_stack(target_inv_name, i) + local stack = target_inv:get_stack(target_inventory_name, i) local item = stack:get_name() if item ~= "" then if hopper_inv:room_for_item("main", item) then local stack_to_take = stack:take_item(1) if target_def.allow_metadata_inventory_take == nil or placer == nil -- backwards compatibility, older versions of this mod didn't record who placed the hopper - or target_def.allow_metadata_inventory_take(target_pos, target_inv_name, i, stack_to_take, placer) > 0 then - target_inv:set_stack(target_inv_name, i, stack) + or target_def.allow_metadata_inventory_take(target_pos, target_inventory_name, i, stack_to_take, placer) > 0 then + target_inv:set_stack(target_inventory_name, i, stack) --add to hopper hopper_inv:add_item("main", stack_to_take) if target_def.on_metadata_inventory_take ~= nil and placer ~= nil then - target_def.on_metadata_inventory_take(target_pos, target_inv_name, i, stack_to_take, placer) + target_def.on_metadata_inventory_take(target_pos, target_inventory_name, i, stack_to_take, placer) end break end @@ -130,20 +112,21 @@ hopper.take_item_from = function(hopper_pos, target_pos, target_node, target_inv end -- Used to put items from the hopper inventory into the target block -hopper.send_item_to = function(hopper_pos, target_pos, target_node, target_inv_info, filtered_items) +hopper.send_item_to = function(hopper_pos, target_pos, target_node, target_inventory_name, filtered_items) + local hopper_meta = minetest.get_meta(hopper_pos) local target_def = minetest.registered_nodes[target_node.name] if not target_def then return false end - local hopper_meta = minetest.get_meta(hopper_pos) local eject_item = hopper.config.eject_button_enabled and hopper_meta:get_string("eject") == "true" and target_def.buildable_to - if not eject_item and not target_inv_info then + if not eject_item and not target_inventory_name then return false end --hopper inventory + local hopper_meta = minetest.get_meta(hopper_pos); local hopper_inv = hopper_meta:get_inventory() if hopper_inv:is_empty("main") == true then return false @@ -151,28 +134,24 @@ hopper.send_item_to = function(hopper_pos, target_pos, target_node, target_inv_i local hopper_inv_size = hopper_inv:get_size("main") local placer = get_placer(hopper_meta:get_string("placer")) - --source inventory - local target_inv_name = target_inv_info.inventory_name - local target_inv = get_container_inventory(target_pos, target_inv_info) - if not target_inv then - return false - end + --target inventory + local target_inv = minetest.get_meta(target_pos):get_inventory() for i = 1,hopper_inv_size do local stack = hopper_inv:get_stack("main", i) local item = stack:get_name() if item ~= "" and (filtered_items == nil or filtered_items[item]) then - if target_inv_name then - if target_inv:room_for_item(target_inv_name, item) then + if target_inventory_name then + if target_inv:room_for_item(target_inventory_name, item) then local stack_to_put = stack:take_item(1) if target_def.allow_metadata_inventory_put == nil or placer == nil -- backwards compatibility, older versions of this mod didn't record who placed the hopper - or target_def.allow_metadata_inventory_put(target_pos, target_inv_name, i, stack_to_put, placer) > 0 then + or target_def.allow_metadata_inventory_put(target_pos, target_inventory_name, i, stack_to_put, placer) > 0 then hopper_inv:set_stack("main", i, stack) --add to target node - target_inv:add_item(target_inv_name, stack_to_put) + target_inv:add_item(target_inventory_name, stack_to_put) if target_def.on_metadata_inventory_put ~= nil and placer ~= nil then - target_def.on_metadata_inventory_put(target_pos, target_inv_name, i, stack_to_put, placer) + target_def.on_metadata_inventory_put(target_pos, target_inventory_name, i, stack_to_put, placer) end return true end