init.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. -- define global
  2. hopper = {}
  3. -- Intllib
  4. local S
  5. if minetest.get_modpath("intllib") then
  6. S = intllib.Getter()
  7. else
  8. S = function(s, a, ...) a = {a, ...}
  9. return s:gsub("@(%d+)", function(n)
  10. return a[tonumber(n)]
  11. end)
  12. end
  13. end
  14. -- creative check
  15. local creative_mode_cache = minetest.settings:get_bool("creative_mode")
  16. function check_creative(name)
  17. return creative_mode_cache or minetest.check_player_privs(name, {creative = true})
  18. end
  19. -- default containers
  20. local containers = {
  21. {"top", "hopper:hopper", "main"},
  22. {"bottom", "hopper:hopper", "main"},
  23. {"side", "hopper:hopper", "main"},
  24. {"side", "hopper:hopper_side", "main"},
  25. {"top", "default:chest", "main"},
  26. {"bottom", "default:chest", "main"},
  27. {"side", "default:chest", "main"},
  28. {"top", "default:furnace", "dst"},
  29. {"bottom", "default:furnace", "src"},
  30. {"side", "default:furnace", "fuel"},
  31. {"top", "default:furnace_active", "dst"},
  32. {"bottom", "default:furnace_active", "src"},
  33. {"side", "default:furnace_active", "fuel"},
  34. {"bottom", "default:chest_locked", "main"},
  35. {"side", "default:chest_locked", "main"},
  36. {"top", "default:chest_open", "main"}, -- new animated chests
  37. {"bottom", "default:chest_open", "main"},
  38. {"side", "default:chest_open", "main"},
  39. {"bottom", "default:chest_locked_open", "main"},
  40. {"side", "default:chest_locked_open", "main"},
  41. {"void", "hopper:hopper", "main"},
  42. {"void", "hopper:hopper_side", "main"},
  43. {"void", "hopper:hopper_void", "main"},
  44. {"void", "default:chest", "main"},
  45. {"void", "default:chest_open", "main"},
  46. {"void", "default:furnace", "src"},
  47. {"void", "default:furnace_active", "src"},
  48. }
  49. -- global function to add new containers
  50. function hopper:add_container(list)
  51. for n = 1, #list do
  52. table.insert(containers, list[n])
  53. end
  54. end
  55. -- protector redo mod support
  56. if minetest.get_modpath("protector") then
  57. hopper:add_container({
  58. {"top", "protector:chest", "main"},
  59. {"bottom", "protector:chest", "main"},
  60. {"side", "protector:chest", "main"},
  61. {"void", "protector:chest", "main"},
  62. })
  63. end
  64. -- wine mod support
  65. if minetest.get_modpath("wine") then
  66. hopper:add_container({
  67. {"top", "wine:wine_barrel", "dst"},
  68. {"bottom", "wine:wine_barrel", "src"},
  69. {"side", "wine:wine_barrel", "src"},
  70. {"void", "wine:wine_barrel", "src"},
  71. })
  72. end
  73. -- formspec
  74. local function get_hopper_formspec(pos)
  75. local spos = pos.x .. "," .. pos.y .. "," ..pos.z
  76. local formspec =
  77. "size[8,9]"
  78. .. default.gui_bg
  79. .. default.gui_bg_img
  80. .. default.gui_slots
  81. .. "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]"
  82. .. "list[current_player;main;0,4.85;8,1;]"
  83. .. "list[current_player;main;0,6.08;8,3;8]"
  84. .. "listring[nodemeta:" .. spos .. ";main]"
  85. .. "listring[current_player;main]"
  86. return formspec
  87. end
  88. -- check where pointing and set normal or side-hopper
  89. local hopper_place = function(itemstack, placer, pointed_thing)
  90. local pos = pointed_thing.above
  91. local x = pointed_thing.under.x - pos.x
  92. local z = pointed_thing.under.z - pos.z
  93. local name = placer:get_player_name() or ""
  94. if minetest.is_protected(pos, name) then
  95. minetest.record_protection_violation(pos, name)
  96. return itemstack
  97. end
  98. if x == -1 then
  99. minetest.set_node(pos, {name = "hopper:hopper_side", param2 = 0})
  100. elseif x == 1 then
  101. minetest.set_node(pos, {name = "hopper:hopper_side", param2 = 2})
  102. elseif z == -1 then
  103. minetest.set_node(pos, {name = "hopper:hopper_side", param2 = 3})
  104. elseif z == 1 then
  105. minetest.set_node(pos, {name = "hopper:hopper_side", param2 = 1})
  106. else
  107. minetest.set_node(pos, {name = "hopper:hopper"})
  108. end
  109. if not check_creative(placer:get_player_name()) then
  110. itemstack:take_item()
  111. end
  112. -- set metadata
  113. local meta = minetest.get_meta(pos)
  114. local inv = meta:get_inventory()
  115. inv:set_size("main", 4*4)
  116. meta:set_string("owner", name)
  117. return itemstack
  118. end
  119. -- hopper
  120. minetest.register_node("hopper:hopper", {
  121. description = S("Hopper (Place onto sides for side-hopper)"),
  122. groups = {cracky = 3},
  123. drawtype = "nodebox",
  124. paramtype = "light",
  125. tiles = {"hopper_top.png", "hopper_top.png", "hopper_front.png"},
  126. inventory_image = "hopper_inv.png",
  127. node_box = {
  128. type = "fixed",
  129. fixed = {
  130. --funnel walls
  131. {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
  132. {0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
  133. {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
  134. {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
  135. --funnel base
  136. {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
  137. --spout
  138. {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
  139. {-0.15, -0.3, -0.15, 0.15, -0.5, 0.15},
  140. },
  141. },
  142. on_place = hopper_place,
  143. can_dig = function(pos, player)
  144. local inv = minetest.get_meta(pos):get_inventory()
  145. return inv:is_empty("main")
  146. end,
  147. on_rightclick = function(pos, node, clicker, itemstack)
  148. if not minetest.get_meta(pos)
  149. or minetest.is_protected(pos, clicker:get_player_name()) then
  150. return itemstack
  151. end
  152. minetest.show_formspec(clicker:get_player_name(),
  153. "hopper:hopper", get_hopper_formspec(pos))
  154. end,
  155. on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  156. minetest.log("action", S("@1 moves stuff in hopper at @2",
  157. player:get_player_name(), minetest.pos_to_string(pos)))
  158. end,
  159. on_metadata_inventory_put = function(pos, listname, index, stack, player)
  160. minetest.log("action", S("@1 moves stuff to hopper at @2",
  161. player:get_player_name(), minetest.pos_to_string(pos)))
  162. end,
  163. on_metadata_inventory_take = function(pos, listname, index, stack, player)
  164. minetest.log("action", S("@1 moves stuff from hopper at @2",
  165. player:get_player_name(), minetest.pos_to_string(pos)))
  166. end,
  167. on_rotate = screwdriver.disallow,
  168. on_blast = function() end,
  169. })
  170. -- side hopper
  171. minetest.register_node("hopper:hopper_side", {
  172. description = S("Side Hopper (Place into crafting to return normal Hopper)"),
  173. groups = {cracky = 3, not_in_creative_inventory = 1},
  174. drawtype = "nodebox",
  175. paramtype = "light",
  176. paramtype2 = "facedir",
  177. tiles = {
  178. "hopper_top.png", "hopper_top.png", "hopper_back.png",
  179. "hopper_side.png", "hopper_back.png", "hopper_back.png"
  180. },
  181. inventory_image = "hopper_side_inv.png",
  182. drop = "hopper:hopper",
  183. node_box = {
  184. type = "fixed",
  185. fixed = {
  186. --funnel walls
  187. {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
  188. {0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
  189. {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
  190. {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
  191. --funnel base
  192. {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
  193. --spout
  194. {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
  195. {-0.7, -0.3, -0.15, 0.15, 0.0, 0.15},
  196. },
  197. },
  198. on_place = hopper_place,
  199. can_dig = function(pos, player)
  200. local inv = minetest.get_meta(pos):get_inventory()
  201. return inv:is_empty("main")
  202. end,
  203. on_rightclick = function(pos, node, clicker, itemstack)
  204. if not minetest.get_meta(pos)
  205. or minetest.is_protected(pos, clicker:get_player_name()) then
  206. return itemstack
  207. end
  208. minetest.show_formspec(clicker:get_player_name(),
  209. "hopper:hopper_side", get_hopper_formspec(pos))
  210. end,
  211. on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  212. minetest.log("action", S("@1 moves stuff in hopper at @2",
  213. player:get_player_name(), minetest.pos_to_string(pos)))
  214. end,
  215. on_metadata_inventory_put = function(pos, listname, index, stack, player)
  216. minetest.log("action", S("@1 moves stuff to hopper at @2",
  217. player:get_player_name(), minetest.pos_to_string(pos)))
  218. end,
  219. on_metadata_inventory_take = function(pos, listname, index, stack, player)
  220. minetest.log("action", S("@1 moves stuff from hopper at @2",
  221. player:get_player_name(), minetest.pos_to_string(pos)))
  222. end,
  223. on_rotate = screwdriver.rotate_simple,
  224. on_blast = function() end,
  225. })
  226. local player_void = {}
  227. -- void hopper
  228. minetest.register_node("hopper:hopper_void", {
  229. description = S("Void Hopper (Use first to set destination container)"),
  230. groups = {cracky = 3},
  231. drawtype = "nodebox",
  232. paramtype = "light",
  233. tiles = {"hopper_top.png", "hopper_top.png", "hopper_front.png"},
  234. inventory_image = "default_obsidian.png^hopper_inv.png",
  235. node_box = {
  236. type = "fixed",
  237. fixed = {
  238. --funnel walls
  239. {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
  240. {0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
  241. {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
  242. {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
  243. --funnel base
  244. {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
  245. },
  246. },
  247. on_use = function(itemstack, player, pointed_thing)
  248. if pointed_thing.type ~= "node" then
  249. return
  250. end
  251. local pos = pointed_thing.under
  252. local name = player:get_player_name()
  253. local node = minetest.get_node(pos).name
  254. local ok
  255. if minetest.is_protected(pos, name) then
  256. minetest.record_protection_violation(pos, name)
  257. return itemstack
  258. end
  259. for _ = 1, #containers do
  260. if node == containers[_][2] then
  261. ok = true
  262. end
  263. end
  264. if ok then
  265. minetest.chat_send_player(name, S("Output container set"
  266. .. " " .. minetest.pos_to_string(pos)))
  267. player_void[name] = pos
  268. else
  269. minetest.chat_send_player(name, S("Not a registered container!"))
  270. player_void[name] = nil
  271. end
  272. end,
  273. on_place = function(itemstack, placer, pointed_thing)
  274. local pos = pointed_thing.above
  275. local name = placer:get_player_name() or ""
  276. if not player_void[name] then
  277. minetest.chat_send_player(name, S("No container position set!"))
  278. return itemstack
  279. end
  280. if minetest.is_protected(pos, name) then
  281. minetest.record_protection_violation(pos, name)
  282. return itemstack
  283. end
  284. if not check_creative(placer:get_player_name()) then
  285. itemstack:take_item()
  286. end
  287. minetest.set_node(pos, {name = "hopper:hopper_void", param2 = 0})
  288. local meta = minetest.get_meta(pos)
  289. local inv = meta:get_inventory()
  290. inv:set_size("main", 4*4)
  291. meta:set_string("owner", name)
  292. meta:set_string("void", minetest.pos_to_string(player_void[name]))
  293. return itemstack
  294. end,
  295. can_dig = function(pos, player)
  296. local inv = minetest.get_meta(pos):get_inventory()
  297. return inv:is_empty("main")
  298. end,
  299. on_rightclick = function(pos, node, clicker, itemstack)
  300. if not minetest.get_meta(pos)
  301. or minetest.is_protected(pos, clicker:get_player_name()) then
  302. return itemstack
  303. end
  304. minetest.show_formspec(clicker:get_player_name(),
  305. "hopper:hopper", get_hopper_formspec(pos))
  306. end,
  307. on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  308. minetest.log("action", S("@1 moves stuff in void hopper at @2",
  309. player:get_player_name(), minetest.pos_to_string(pos)))
  310. end,
  311. on_metadata_inventory_put = function(pos, listname, index, stack, player)
  312. minetest.log("action", S("@1 moves stuff into void hopper at @2",
  313. player:get_player_name(), minetest.pos_to_string(pos)))
  314. end,
  315. on_metadata_inventory_take = function(pos, listname, index, stack, player)
  316. minetest.log("action", S("@1 moves stuff from void hopper at @2",
  317. player:get_player_name(), minetest.pos_to_string(pos)))
  318. end,
  319. on_rotate = screwdriver.disallow,
  320. on_blast = function() end,
  321. })
  322. -- transfer function
  323. local transfer = function(src, srcpos, dst, dstpos)
  324. -- source inventory
  325. local inv = minetest.get_meta(srcpos):get_inventory()
  326. -- destination inventory
  327. local inv2 = minetest.get_meta(dstpos):get_inventory()
  328. -- check for empty source or no inventory
  329. if not inv or not inv2 or inv:is_empty(src) == true then
  330. return
  331. end
  332. local stack, item
  333. -- transfer item
  334. for i = 1, inv:get_size(src) do
  335. stack = inv:get_stack(src, i)
  336. item = stack:get_name()
  337. -- if slot not empty and room for item in destination
  338. if item ~= ""
  339. and inv2:room_for_item(dst, item) then
  340. -- is item a tool
  341. if stack:get_wear() > 0 then
  342. inv2:add_item(dst, stack:take_item(stack:get_count()))
  343. inv:set_stack(src, i, nil)
  344. else -- not a tool
  345. stack:take_item(1)
  346. inv2:add_item(dst, item)
  347. inv:set_stack(src, i, stack)
  348. end
  349. return
  350. end
  351. end
  352. end
  353. -- hopper workings
  354. minetest.register_abm({
  355. label = "Hopper suction and transfer",
  356. nodenames = {"hopper:hopper", "hopper:hopper_side", "hopper:hopper_void"},
  357. interval = 1.0,
  358. chance = 1,
  359. catch_up = false,
  360. action = function(pos, node, active_object_count, active_object_count_wider)
  361. local inv = minetest.get_meta(pos):get_inventory()
  362. for _,object in pairs(minetest.get_objects_inside_radius(pos, 1)) do
  363. if not object:is_player()
  364. and object:get_luaentity()
  365. and object:get_luaentity().name == "__builtin:item"
  366. and inv
  367. and inv:room_for_item("main",
  368. ItemStack(object:get_luaentity().itemstring)) then
  369. if object:get_pos().y - pos.y >= 0.3 then
  370. inv:add_item("main",
  371. ItemStack(object:get_luaentity().itemstring))
  372. object:get_luaentity().itemstring = ""
  373. object:remove()
  374. end
  375. end
  376. end
  377. local front
  378. -- if side hopper check which way spout is facing
  379. if node.name == "hopper:hopper_side" then
  380. local face = minetest.get_node(pos).param2
  381. if face == 0 then
  382. front = {x = pos.x - 1, y = pos.y, z = pos.z}
  383. elseif face == 1 then
  384. front = {x = pos.x, y = pos.y, z = pos.z + 1}
  385. elseif face == 2 then
  386. front = {x = pos.x + 1, y = pos.y, z = pos.z}
  387. elseif face == 3 then
  388. front = {x = pos.x, y = pos.y, z = pos.z - 1}
  389. else
  390. return
  391. end
  392. elseif node.name == "hopper:hopper_void" then
  393. local meta = minetest.get_meta(pos)
  394. if not meta then return end
  395. front = minetest.string_to_pos(meta:get_string("void"))
  396. elseif node.name == "hopper:hopper" then
  397. -- otherwise normal hopper, output downwards
  398. front = {x = pos.x, y = pos.y - 1, z = pos.z}
  399. else
  400. return
  401. end
  402. -- get node above hopper
  403. local top = minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z}).name
  404. -- get node at other end of spout
  405. local out = minetest.get_node(front).name
  406. local where, nod, inv, def
  407. -- do for loop here for api check
  408. for n = 1, #containers do
  409. where = containers[n][1]
  410. nod = containers[n][2]
  411. inv = containers[n][3]
  412. -- from top node into hopper below
  413. if where == "top" and top == nod
  414. and (node.name == "hopper:hopper"
  415. or node.name == "hopper:hopper_side"
  416. or node.name == "hopper:hopper_void") then
  417. transfer(inv, {x = pos.x, y = pos.y + 1, z = pos.z}, "main", pos)
  418. minetest.get_node_timer(
  419. {x = pos.x, y = pos.y + 1, z = pos.z}):start(1)--0.5)
  420. -- from top hopper into node below
  421. elseif where == "bottom" and out == nod
  422. and node.name == "hopper:hopper" then
  423. transfer("main", pos, inv, front)
  424. minetest.get_node_timer(front):start(1)--0.5)
  425. -- side hopper into container beside
  426. elseif where == "side" and out == nod
  427. and node.name == "hopper:hopper_side" then
  428. transfer("main", pos, inv, front)
  429. minetest.get_node_timer(front):start(1)--0.5)
  430. -- void hopper to destination container
  431. elseif where == "void" and out == nod
  432. and node.name == "hopper:hopper_void" then
  433. transfer("main", pos, inv, front)
  434. minetest.get_node_timer(front):start(1)--0.5)
  435. end
  436. end
  437. end,
  438. })
  439. -- hopper recipe
  440. minetest.register_craft({
  441. output = "hopper:hopper",
  442. recipe = {
  443. {"default:steel_ingot", "default:chest", "default:steel_ingot"},
  444. {"", "default:steel_ingot", ""},
  445. },
  446. })
  447. -- side hopper to hopper recipe
  448. minetest.register_craft({
  449. type = "shapeless",
  450. output = "hopper:hopper",
  451. recipe = {"hopper:hopper_side"},
  452. })
  453. -- void hopper recipe
  454. if minetest.get_modpath("teleport_potion") then
  455. minetest.register_craft({
  456. output = "hopper:hopper_void",
  457. recipe = {
  458. {"default:steel_ingot", "default:chest", "default:steel_ingot"},
  459. {"teleport_potion:potion", "default:steel_ingot", "teleport_potion:potion"},
  460. },
  461. })
  462. else
  463. minetest.register_craft({
  464. output = "hopper:hopper_void",
  465. recipe = {
  466. {"default:steel_ingot", "default:chest", "default:steel_ingot"},
  467. {"default:diamondblock", "default:steel_ingot", "default:mese"},
  468. },
  469. })
  470. end
  471. -- add lucky blocks
  472. if minetest.get_modpath("lucky_block") then
  473. lucky_block:add_blocks({
  474. {"dro", {"hopper:hopper"}, 3},
  475. {"nod", "default:lava_source", 1},
  476. })
  477. end
  478. print (S("[MOD] Hopper loaded"))