sorter.lua 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. -- internationalization boilerplate
  2. local MP = minetest.get_modpath(minetest.get_current_modname())
  3. local S, NS = dofile(MP.."/intllib.lua")
  4. local facedir_to_bottomdir = {
  5. [0]={x=0, y=-1, z=0},
  6. {x=0, y=0, z=-1},
  7. {x=0, y=0, z=1},
  8. {x=-1, y=0, z=0},
  9. {x=1, y=0, z=0},
  10. {x=0, y=1, z=0},
  11. }
  12. local bottomdir = function(facedir)
  13. return facedir_to_bottomdir[math.floor(facedir/4)]
  14. end
  15. local function get_sorter_formspec(pos)
  16. local spos = hopper.get_string_pos(pos)
  17. local filter_all = minetest.get_meta(pos):get_string("filter_all") == "true"
  18. local y_displace = 0
  19. local filter_button_text, filter_button_tooltip, filter_body
  20. if filter_all then
  21. filter_body = ""
  22. filter_button_text = S("Selective\nFilter")
  23. filter_button_tooltip = S("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.")
  24. else
  25. filter_body = "label[3.7,0;"..S("Filter").."]list[nodemeta:" .. spos .. ";filter;0,0.5;8,1;]"
  26. filter_button_text = S("Filter\nAll")
  27. filter_button_tooltip = S("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.")
  28. y_displace = 1.6
  29. end
  30. local formspec =
  31. "size[8," .. 7 + y_displace .. "]"
  32. .. hopper.formspec_bg
  33. .. filter_body
  34. .. "list[nodemeta:" .. spos .. ";main;3,".. tostring(0.3 + y_displace) .. ";2,2;]"
  35. .. "button_exit[7,".. tostring(0.8 + y_displace) .. ";1,1;filter_all;".. filter_button_text .. "]tooltip[filter_all;" .. filter_button_tooltip.. "]"
  36. .. hopper.get_eject_button_texts(pos, 6, 0.8 + y_displace)
  37. .. "list[current_player;main;0,".. tostring(2.85 + y_displace) .. ";8,1;]"
  38. .. "list[current_player;main;0,".. tostring(4.08 + y_displace) .. ";8,3;8]"
  39. .. "listring[nodemeta:" .. spos .. ";main]"
  40. .. "listring[current_player;main]"
  41. return formspec
  42. end
  43. minetest.register_node("hopper:sorter", {
  44. description = S("Sorter"),
  45. _doc_items_longdesc = hopper.doc.sorter_long_desc,
  46. _doc_items_usagehelp = hopper.doc.sorter_usage,
  47. groups = {cracky = 3},
  48. sounds = hopper.metal_sounds,
  49. drawtype = "nodebox",
  50. paramtype = "light",
  51. paramtype2 = "facedir",
  52. tiles = {
  53. "hopper_bottom_" .. hopper.config.texture_resolution .. ".png",
  54. "hopper_top_" .. hopper.config.texture_resolution .. ".png",
  55. "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",
  56. "hopper_bottom_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_arrow_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_sub_arrow_" .. hopper.config.texture_resolution .. ".png",
  57. "hopper_top_" .. hopper.config.texture_resolution .. ".png",
  58. "hopper_bottom_" .. hopper.config.texture_resolution .. ".png^hopper_sorter_arrow_" .. hopper.config.texture_resolution .. ".png",
  59. },
  60. use_texture_alpha = "clip",
  61. node_box = {
  62. type = "fixed",
  63. fixed = {
  64. {-0.3, -0.3, -0.4, 0.3, 0.4, 0.4},
  65. {-0.2, -0.2, 0.4, 0.2, 0.2, 0.7},
  66. {-0.2, -0.3, -0.2, 0.2, -0.7, 0.2},
  67. },
  68. },
  69. on_construct = function(pos)
  70. local meta = minetest.get_meta(pos)
  71. local inv = meta:get_inventory()
  72. inv:set_size("main", 2*2)
  73. inv:set_size("filter", 8)
  74. end,
  75. on_place = function(itemstack, placer, pointed_thing, node_name)
  76. local pos = pointed_thing.under
  77. local pos2 = pointed_thing.above
  78. local x = pos.x - pos2.x
  79. local z = pos.z - pos2.z
  80. if not placer then
  81. return itemstack
  82. end
  83. if minetest.is_protected(pos, placer:get_player_name()) then
  84. return itemstack
  85. end
  86. local returned_stack, success = minetest.item_place_node(itemstack, placer, pointed_thing)
  87. if success then
  88. local meta = minetest.get_meta(pos2)
  89. meta:set_string("placer", placer:get_player_name())
  90. end
  91. return returned_stack
  92. end,
  93. can_dig = function(pos,player)
  94. local meta = minetest.get_meta(pos);
  95. local inv = meta:get_inventory()
  96. return inv:is_empty("main")
  97. end,
  98. on_rightclick = function(pos, node, clicker, itemstack)
  99. if minetest.is_protected(pos, clicker:get_player_name()) and not minetest.check_player_privs(clicker, "protection_bypass") then
  100. return
  101. end
  102. minetest.show_formspec(clicker:get_player_name(),
  103. "hopper_formspec:"..minetest.pos_to_string(pos), get_sorter_formspec(pos))
  104. end,
  105. allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  106. if listname == "filter" then
  107. local inv = minetest.get_inventory({type="node", pos=pos})
  108. inv:set_stack(listname, index, stack:take_item(1))
  109. return 0
  110. end
  111. return stack:get_count()
  112. end,
  113. allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  114. if listname == "filter" then
  115. local inv = minetest.get_inventory({type="node", pos=pos})
  116. inv:set_stack(listname, index, ItemStack(""))
  117. return 0
  118. end
  119. return stack:get_count()
  120. end,
  121. allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  122. if to_list == "filter" then
  123. local inv = minetest.get_inventory({type="node", pos=pos})
  124. local stack_moved = inv:get_stack(from_list, from_index)
  125. inv:set_stack(to_list, to_index, stack_moved:take_item(1))
  126. return 0
  127. elseif from_list == "filter" then
  128. local inv = minetest.get_inventory({type="node", pos=pos})
  129. inv:set_stack(from_list, from_index, ItemStack(""))
  130. return 0
  131. end
  132. return count
  133. end,
  134. on_metadata_inventory_put = function(pos, listname, index, stack, player)
  135. minetest.log("action", S("@1 moves stuff to sorter at @2",
  136. player:get_player_name(), minetest.pos_to_string(pos)))
  137. local timer = minetest.get_node_timer(pos)
  138. if not timer:is_started() then
  139. timer:start(1)
  140. end
  141. end,
  142. on_timer = function(pos, elapsed)
  143. local meta = minetest.get_meta(pos);
  144. local inv = meta:get_inventory()
  145. -- build a filter list
  146. local filter_items = nil
  147. if meta:get_string("filter_all") ~= "true" then
  148. filter_items = {}
  149. local filter_inv_size = inv:get_size("filter")
  150. for i = 1, filter_inv_size do
  151. local stack = inv:get_stack("filter", i)
  152. local item = stack:get_name()
  153. if item ~= "" then
  154. filter_items[item] = true
  155. end
  156. end
  157. end
  158. local node = minetest.get_node(pos)
  159. local dir = minetest.facedir_to_dir(node.param2)
  160. local default_destination_pos = vector.add(pos, dir)
  161. local default_output_direction
  162. if dir.y == 0 then
  163. default_output_direction = "horizontal"
  164. end
  165. dir = bottomdir(node.param2)
  166. local filter_destination_pos = vector.add(pos, dir)
  167. local filter_output_direction
  168. if dir.y == 0 then
  169. filter_output_direction = "horizontal"
  170. end
  171. local success = false
  172. local filter_destination_node = minetest.get_node(filter_destination_pos)
  173. local registered_inventories = hopper.get_registered_inventories_for(filter_destination_node.name)
  174. if registered_inventories ~= nil then
  175. if filter_output_direction == "horizontal" then
  176. success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, registered_inventories["side"], filter_items)
  177. else
  178. success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, registered_inventories["bottom"], filter_items)
  179. end
  180. else
  181. success = hopper.send_item_to(pos, filter_destination_pos, filter_destination_node, nil, filter_items)
  182. end
  183. 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.
  184. local default_destination_node = minetest.get_node(default_destination_pos)
  185. local registered_inventories = hopper.get_registered_inventories_for(default_destination_node.name)
  186. if registered_inventories ~= nil then
  187. if default_output_direction == "horizontal" then
  188. hopper.send_item_to(pos, default_destination_pos, default_destination_node, registered_inventories["side"])
  189. else
  190. hopper.send_item_to(pos, default_destination_pos, default_destination_node, registered_inventories["bottom"])
  191. end
  192. else
  193. hopper.send_item_to(pos, default_destination_pos, default_destination_node)
  194. end
  195. end
  196. if not inv:is_empty("main") then
  197. minetest.get_node_timer(pos):start(1)
  198. end
  199. end,
  200. })