local S = minetest.get_translator("hopper") local FS = hopper.translator_escaped local facedir_to_bottomdir = { [0]={x=0, y=-1, z=0}, {x=0, y=0, z=-1}, {x=0, y=0, z=1}, {x=-1, y=0, z=0}, {x=1, y=0, z=0}, {x=0, y=1, z=0}, } local bottomdir = function(facedir) return facedir_to_bottomdir[math.floor(facedir/4)] 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 if filter_all then filter_body = "" filter_texture = "hopper_mode_off.png" filter_button_tooltip = FS("This sorter is currently set to try sending all items\nin the direction of the arrow. Click this button\nto enable an item-type-specific filter.") else filter_body = "label[3.7,0;"..FS("Filter").."]list[nodemeta:" .. spos .. ";filter;0,0.5;8,1;]" filter_texture = "hopper_mode_on.png" 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 .. "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.. "]" .. hopper.get_eject_button_texts(pos, 6, 0.8 + y_displace) .. "list[current_player;main;0,".. tostring(2.85 + y_displace) .. ";8,1;]" .. "list[current_player;main;0,".. tostring(4.08 + y_displace) .. ";8,3;8]" .. "listring[nodemeta:" .. spos .. ";main]" .. "listring[current_player;main]" return formspec end minetest.register_node("hopper:sorter", { description = S("Sorter"), _doc_items_longdesc = hopper.doc.sorter_long_desc, _doc_items_usagehelp = hopper.doc.sorter_usage, groups = {cracky = 3}, sounds = hopper.metal_sounds, drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", tiles = { "hopper_bottom_" .. hopper.config.texture_resolution .. ".png", "hopper_top_" .. hopper.config.texture_resolution .. ".png", "hopper_bottom_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_arrow_" .. hopper.config.texture_resolution .. ".png^[transformFX^hopper_sorter_sub_arrow_" .. hopper.config.texture_resolution .. ".png^[transformFX", "hopper_bottom_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_arrow_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_sub_arrow_" .. hopper.config.texture_resolution .. ".png", "hopper_top_" .. hopper.config.texture_resolution .. ".png", "hopper_bottom_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_arrow_" .. hopper.config.texture_resolution .. ".png", }, node_box = { type = "fixed", fixed = { {-0.3, -0.3, -0.4, 0.3, 0.4, 0.4}, {-0.2, -0.2, 0.4, 0.2, 0.2, 0.7}, {-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 local meta = minetest.get_meta(pos2) meta:set_string("placer", placer:get_player_name()) 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 end 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}) inv:set_stack(listname, index, stack:take_item(1)) return 0 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}) inv:set_stack(listname, index, ItemStack("")) return 0 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}) local stack_moved = inv:get_stack(from_list, from_index) inv:set_stack(to_list, to_index, stack_moved:take_item(1)) return 0 elseif from_list == "filter" then local inv = minetest.get_inventory({type="node", pos=pos}) inv:set_stack(from_list, from_index, ItemStack("")) 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))) local timer = minetest.get_node_timer(pos) if not timer:is_started() then timer:start(1) 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 if meta:get_string("filter_all") ~= "true" then filter_items = {} local filter_inv_size = inv:get_size("filter") for i = 1, filter_inv_size do local stack = inv:get_stack("filter", i) local item = stack:get_name() if item ~= "" then filter_items[item] = true 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 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 if dir.y == 0 then filter_output_direction = "horizontal" end 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 else success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, nil, filter_items) end 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 end, })