leecher.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. if not minetest.global_exists("leecher") then leecher = {} end
  2. leecher.data = leecher.data or {}
  3. leecher.modpath = minetest.get_modpath("machines")
  4. leecher.liquid_range = 20
  5. -- Localize for performance.
  6. local math_random = math.random
  7. local BUFFER_SIZE = tech.leecher.buffer
  8. local ENERGY_AMOUNT = tech.leecher.power
  9. local DISSOLVE_HEIGHT = 120
  10. -- Per testing, this function will load a node from any position on the map as
  11. -- long as it was previously generated.
  12. local function safe_get_node(p)
  13. local n = minetest.get_node_or_nil(p)
  14. if not n then
  15. -- Load map and get node.
  16. -- This will still return "ignore" if the location was never generated.
  17. local v = VoxelManip(p, p)
  18. n = v:get_node_at(p)
  19. end
  20. return n
  21. end
  22. -- What results should machine give for these ore types?
  23. -- Ores without an entry here will produce nothing!
  24. local ore_conversion_data = {
  25. ["akalin:ore"] = "akalin:dust",
  26. ["alatro:ore"] = "alatro:dust",
  27. ["arol:ore"] = "arol:dust",
  28. ["chromium:ore"] = "chromium:dust",
  29. ["kalite:ore"] = "kalite:dust",
  30. ["lead:ore"] = "lead:dust",
  31. ["default:stone_with_iron"] = "dusts:iron",
  32. ["default:stone_with_coal"] = "dusts:coal",
  33. ["default:stone_with_copper"] = "dusts:copper",
  34. ["default:stone_with_gold"] = "dusts:gold",
  35. ["default:stone_with_diamond"] = "dusts:diamond",
  36. ["default:desert_stone_with_coal"] = "dusts:coal",
  37. ["default:desert_stone_with_iron"] = "dusts:iron",
  38. ["default:desert_stone_with_copper"] = "dusts:copper",
  39. ["default:desert_stone_with_diamond"] = "dusts:diamond",
  40. ["moreores:mineral_tin"] = "dusts:tin",
  41. ["moreores:mineral_silver"] = "dusts:silver",
  42. ["moreores:mineral_mithril"] = "dusts:mithril",
  43. ["glowstone:glowstone"] = "glowstone:glowing_dust",
  44. ["glowstone:minerals"] = "glowstone:glowing_dust",
  45. ["glowstone:luxore"] = "glowstone:glowing_dust",
  46. ["rackstone:redrack_with_coal"] = "dusts:coal",
  47. ["rackstone:redrack_with_iron"] = "dusts:iron",
  48. ["rackstone:redrack_with_copper"] = "dusts:copper",
  49. ["rackstone:redrack_with_tin"] = "dusts:tin",
  50. ["talinite:ore"] = "talinite:dust",
  51. ["thorium:ore"] = "thorium:dust",
  52. ["uranium:ore"] = "uranium:dust",
  53. ["zinc:ore"] = "zinc:dust",
  54. ["sulfur:ore"] = "sulfur:dust",
  55. }
  56. -- Nodes listed here are dissolved without any result.
  57. local ore_dissolve_data = {
  58. ["default:stone"] = true,
  59. ["default:desert_stone"] = true,
  60. ["rackstone:redrack"] = true,
  61. ["rackstone:rackstone"] = true,
  62. ["rackstone:mg_redrack"] = true,
  63. ["rackstone:mg_rackstone"] = true,
  64. }
  65. -- Nodes listed here can only be obtained by dropping them via ceiling-cavitation.
  66. local ore_drop_data = {
  67. ["gems:ruby_ore"] = true,
  68. ["gems:emerald_ore"] = true,
  69. ["gems:sapphire_ore"] = true,
  70. ["gems:amethyst_ore"] = true,
  71. ["default:clay"] = true,
  72. ["default:sand"] = true,
  73. ["default:dirt"] = true,
  74. ["default:gravel"] = true,
  75. ["default:stone_with_mese"] = true,
  76. ["default:mese"] = true,
  77. ["rackstone:dauthsand"] = true,
  78. ["rackstone:blackrack"] = true,
  79. ["rackstone:bluerack"] = true,
  80. ["quartz:quartz_ore"] = true,
  81. ["titanium:ore"] = true,
  82. ["morerocks:marble"] = true,
  83. ["morerocks:marble_pink"] = true,
  84. ["morerocks:marble_white"] = true,
  85. ["morerocks:granite"] = true,
  86. ["morerocks:serpentine"] = true,
  87. ["luxore:luxore"] = true,
  88. }
  89. -- Queued algorithm.
  90. local function get_water_surface(startpos)
  91. local traversal = {}
  92. local queue = {}
  93. local output = {}
  94. local curpos, hash, exists, name, found, depth, is_water, is_leecher
  95. local first = true
  96. local get_node_hash = minetest.hash_node_position
  97. local get_node = minetest.get_node
  98. local max_depth = leecher.liquid_range
  99. startpos.d = 1
  100. queue[#queue+1] = startpos
  101. ::continue::
  102. curpos = queue[#queue]
  103. queue[#queue] = nil
  104. depth = curpos.d
  105. curpos.d = nil
  106. hash = get_node_hash(curpos)
  107. exists = false
  108. if traversal[hash] then
  109. exists = true
  110. if depth >= traversal[hash] then
  111. goto next
  112. end
  113. end
  114. if depth >= max_depth then
  115. goto next
  116. end
  117. name = get_node(curpos).name
  118. found = false
  119. is_water = false
  120. is_leecher = false
  121. if name == "default:water_source" or name == "default:river_water_source" then
  122. is_water = true
  123. found = true
  124. end
  125. if name == "leecher:leecher" then
  126. is_leecher = true
  127. found = true
  128. end
  129. -- Water must have air above.
  130. if found and is_water then
  131. if get_node(vector.add(curpos, {x=0, y=1, z=0})).name ~= "air" then
  132. found = false
  133. end
  134. end
  135. if not found then
  136. goto next
  137. end
  138. traversal[hash] = depth
  139. if not exists then
  140. if is_water or is_leecher then
  141. output[#output+1] = curpos
  142. end
  143. end
  144. -- Queue up adjacent locations.
  145. -- We only search horizontally.
  146. queue[#queue+1] = {x=curpos.x+1, y=curpos.y, z=curpos.z, d=depth+1}
  147. queue[#queue+1] = {x=curpos.x-1, y=curpos.y, z=curpos.z, d=depth+1}
  148. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z+1, d=depth+1}
  149. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z-1, d=depth+1}
  150. ::next::
  151. first = false
  152. if #queue > 0 then
  153. goto continue
  154. end
  155. -- Max water count is ~144.
  156. --minetest.chat_send_all("water: " .. #output)
  157. return output
  158. end
  159. local function do_water_boiling(pos)
  160. local nn = minetest.get_node(pos).name
  161. if nn == "default:water_source" or nn == "default:river_water_source" then
  162. local bubbles = {
  163. amount = math_random(3, 5),
  164. time = 1.0,
  165. minpos = vector.add(pos, {x=-1, y=0.5, z=-1}),
  166. maxpos = vector.add(pos, {x=1, y=0.5, z=1}),
  167. minvel = vector.new(-0.2, 1.0, -0.2),
  168. maxvel = vector.new(0.2, 5.0, 0.2),
  169. minacc = vector.new(0.0, -1, 0.0),
  170. maxacc = vector.new(0.0, -5, 0.0),
  171. minexptime = 0.5,
  172. maxexptime = 2,
  173. minsize = 0.5,
  174. maxsize = 1.5,
  175. collisiondetection = true,
  176. collision_removal = true,
  177. vertical = false,
  178. texture = "bubble.png",
  179. }
  180. minetest.add_particlespawner(bubbles)
  181. end
  182. end
  183. local function check_outsalting_done(heights)
  184. local reached_max = false
  185. local count = 0
  186. local size = 0
  187. for k, v in pairs(heights) do
  188. size = size + 1
  189. if v >= DISSOLVE_HEIGHT then
  190. count = count + 1
  191. end
  192. end
  193. if count == size and size > 0 then
  194. reached_max = true
  195. end
  196. return reached_max
  197. end
  198. -- Dig ceiling above position.
  199. local function do_ceiling_dig(pos, heights)
  200. local hash = minetest.hash_node_position(pos)
  201. local yheight = heights[hash] or 0
  202. yheight = yheight + 1
  203. local p3 = vector.new(pos)
  204. p3.y = p3.y + yheight
  205. while safe_get_node(p3).name == "air" do
  206. p3.y = p3.y + 1
  207. yheight = yheight + 1
  208. end
  209. heights[hash] = yheight
  210. if yheight >= DISSOLVE_HEIGHT then
  211. return false, nil
  212. end
  213. if true then
  214. local p = {x=pos.x, y=pos.y+yheight, z=pos.z}
  215. local node = safe_get_node(p)
  216. if node.name == "air" then
  217. -- Keep going.
  218. elseif node.name == "ignore" then
  219. -- Stop on finding "ignore".
  220. return false, nil
  221. elseif ore_dissolve_data[node.name] then
  222. if not minetest.test_protection(p, "") then
  223. -- Dissolve node without giving any result.
  224. minetest.remove_node(p)
  225. minetest.check_for_falling(p)
  226. return true, nil
  227. else
  228. return false, nil
  229. end
  230. elseif ore_conversion_data[node.name] then
  231. -- If we encounter a dissolvable node, dissolve it.
  232. if not minetest.test_protection(p, "") then
  233. minetest.remove_node(p)
  234. minetest.check_for_falling(p)
  235. local count = math_random(4, 16)
  236. -- Any param2 value other than 0 indicates this node was NOT placed by
  237. -- the mapgen.
  238. if node.param2 ~= 0 then
  239. count = math_random(4, 8)
  240. end
  241. local stack = ItemStack(ore_conversion_data[node.name] .. " " .. count)
  242. if stack:is_known() then
  243. return true, stack
  244. else
  245. return false, nil
  246. end
  247. else
  248. return false, nil
  249. end
  250. elseif ore_drop_data[node.name] then
  251. if not minetest.test_protection(p, "") then
  252. -- Undissolvable node, just drop it.
  253. sfn.drop_node(p)
  254. minetest.check_for_falling(p)
  255. return true, nil
  256. else
  257. return false, nil
  258. end
  259. else
  260. -- If it's protected, we ran into a developed zone, the machine should go
  261. -- into standby mode.
  262. if minetest.test_protection(p, "") then
  263. return false, nil
  264. end
  265. -- Unrecognized node, drop it.
  266. sfn.drop_node(p)
  267. minetest.check_for_falling(p)
  268. return true, nil
  269. end
  270. end
  271. return true, nil
  272. end
  273. leecher.compose_formspec =
  274. function(pos)
  275. local formspec =
  276. "size[8,4.5]" ..
  277. default.gui_bg ..
  278. default.gui_bg_img ..
  279. default.gui_slots ..
  280. "label[0.5,1.5;Energy Buffer]" ..
  281. "list[context;buffer;0.5,2;1,1]" ..
  282. "list[context;main;4.5,0;3,3]" ..
  283. "list[current_player;main;0,3.5;8,1;]" ..
  284. "listring[context;main]" ..
  285. "listring[current_player;main]" ..
  286. default.get_hotbar_bg(0, 3.5)
  287. local meta = minetest.get_meta(pos)
  288. if meta:get_int("enabled") == 1 then
  289. formspec = formspec ..
  290. "label[0.5,0;Outsalter Enabled]" ..
  291. "button[0.5,0.4;2.5,1;toggle;Disable Machine]"
  292. else
  293. formspec = formspec ..
  294. "label[0.5,0;Outsalter Disabled]" ..
  295. "button[0.5,0.4;2.5,1;toggle;Enable Machine]"
  296. end
  297. return formspec
  298. end
  299. leecher.on_receive_fields =
  300. function(pos, formname, fields, sender)
  301. if minetest.test_protection(pos, sender:get_player_name()) then
  302. return
  303. end
  304. local meta = minetest.get_meta(pos)
  305. if fields.toggle then
  306. if meta:get_int("enabled") == 1 then
  307. meta:set_int("enabled", 0)
  308. else
  309. meta:set_int("enabled", 1)
  310. end
  311. leecher.trigger_update(pos)
  312. -- Only need update if something changed.
  313. meta:set_string("formspec", leecher.compose_formspec(pos))
  314. end
  315. end
  316. leecher.compose_infotext =
  317. function(pos)
  318. local meta = minetest.get_meta(pos)
  319. local state = "Standby"
  320. local eups = 0
  321. if meta:get_int("active") == 1 then
  322. state = "Active"
  323. eups = meta:get_int("eups_usage")
  324. end
  325. local infotext = "HV Mineral Outsalter (" .. state .. ")\n" ..
  326. "Demand: " .. eups .. " EU Per/Sec"
  327. local errstr = meta:get_string("error")
  328. if errstr ~= "" and errstr ~= "DUMMY" then
  329. infotext = infotext .. "\nMessage: " .. errstr
  330. end
  331. return infotext
  332. end
  333. leecher.trigger_update =
  334. function(pos)
  335. local timer = minetest.get_node_timer(pos)
  336. -- Restart timer even if already running.
  337. timer:start(1.0)
  338. end
  339. leecher.on_punch =
  340. function(pos, node, puncher, pointed_thing)
  341. leecher.trigger_update(pos)
  342. end
  343. leecher.can_dig =
  344. function(pos, player)
  345. local meta = minetest.get_meta(pos)
  346. local inv = meta:get_inventory()
  347. return inv:is_empty("main")
  348. end
  349. leecher.on_timer =
  350. function(pos, elapsed)
  351. --minetest.chat_send_all("# Server: Elapsed time is " .. elapsed .. "!")
  352. local keeprunning = false
  353. local meta = minetest.get_meta(pos)
  354. local inv = meta:get_inventory()
  355. local hash = minetest.hash_node_position(pos)
  356. local try_run = false
  357. -- If the machine is enabled, keep running.
  358. if meta:get_int("enabled") == 1 then
  359. try_run = true
  360. meta:set_string("error", "DUMMY")
  361. end
  362. if try_run then
  363. -- Assuming we can keep running unless someone says otherwise.
  364. keeprunning = true
  365. -- Ensure external datatable exists.
  366. if not leecher.data[hash] then
  367. leecher.data[hash] = {
  368. timer2 = 10,
  369. timer3 = math_random(1, 60),
  370. water = {},
  371. heights = {},
  372. }
  373. end
  374. -- Bring datatable local.
  375. local data = leecher.data[hash]
  376. -- Consume energy.
  377. do
  378. local energy_usage = #(data.water or {}) * ENERGY_AMOUNT
  379. if energy_usage < 200 then energy_usage = 200 end
  380. meta:set_int("eups_usage", energy_usage)
  381. local energy = inv:get_stack("buffer", 1)
  382. --energy = ItemStack("atomic:energy 60000")
  383. if energy:get_count() >= energy_usage then
  384. energy:set_count(energy:get_count() - energy_usage)
  385. inv:set_stack("buffer", 1, energy)
  386. else
  387. -- Try to get energy from network.
  388. local owner = meta:get_string("owner")
  389. local gotten = net2.get_energy(pos, owner, BUFFER_SIZE, "hv")
  390. if gotten >= energy_usage then
  391. energy = ItemStack("atomic:energy " .. (energy:get_count() + gotten))
  392. inv:set_stack("buffer", 1, energy)
  393. -- Wait for next iteration before producing again.
  394. goto cancel
  395. end
  396. -- Not enough energy!
  397. meta:set_string("error", "Insufficient power.")
  398. meta:set_int("enabled", 0)
  399. keeprunning = false
  400. goto cancel
  401. end
  402. end
  403. if not data.water or #(data.water) == 0 then
  404. meta:set_string("error", "Warming up (" .. data.timer3 .. ") ...")
  405. end
  406. -- Update water surface detection every so often.
  407. data.timer3 = data.timer3 - 1
  408. if data.timer3 < 0 then
  409. data.timer3 = math_random(30, 60*3)
  410. data.water = get_water_surface(pos)
  411. data.heights = {}
  412. -- Initialize the height data.
  413. for k, v in ipairs(data.water) do
  414. local hash = minetest.hash_node_position(v)
  415. data.heights[hash] = 0
  416. end
  417. if #(data.water) < 2 then
  418. meta:set_string("error", "No outsalting fluid!")
  419. meta:set_int("enabled", 0)
  420. data.water = {}
  421. data.heights = {}
  422. keeprunning = false
  423. goto cancel
  424. end
  425. end
  426. -- Spawn boiling particles.
  427. if #(data.water) > 0 then
  428. local rnd = math_random(1, 3)
  429. for i = 1, rnd, 1 do
  430. local p2 = data.water[math_random(1, #(data.water))]
  431. minetest.after(math_random(1, 10) / 10, do_water_boiling, p2)
  432. end
  433. end
  434. -- Dig ceiling.
  435. data.timer2 = data.timer2 - 1
  436. if data.timer2 < 0 then
  437. data.timer2 = 10
  438. if #(data.water) > 0 then
  439. local idx = math_random(1, #(data.water))
  440. local p2 = data.water[idx]
  441. local success, rstack = do_ceiling_dig(p2, data.heights)
  442. if not success then
  443. table.remove(data.water, idx)
  444. -- First, check if outsalting reached its max height.
  445. local reached_max = check_outsalting_done(data.heights)
  446. if reached_max then
  447. meta:set_string("error", "Finished.")
  448. meta:set_int("enabled", 0)
  449. keeprunning = false
  450. goto cancel
  451. end
  452. -- Check if we ran out of vertical columns.
  453. -- This could happen before reaching max height if we ran into a
  454. -- protected region.
  455. if #(data.water) == 0 then
  456. meta:set_string("error", "Outsalting aborted!")
  457. meta:set_int("enabled", 0)
  458. keeprunning = false
  459. goto cancel
  460. end
  461. end
  462. if rstack then
  463. if inv:room_for_item("main", rstack) then
  464. inv:add_item("main", rstack)
  465. else
  466. meta:set_string("error", "Inventory full.")
  467. meta:set_int("enabled", 0)
  468. keeprunning = false
  469. goto cancel
  470. end
  471. end
  472. end
  473. end
  474. end
  475. -- Jump here if something prevents machine from working.
  476. ::cancel::
  477. -- Determine mode (active or sleep) and set timer accordingly.
  478. if keeprunning then
  479. minetest.get_node_timer(pos):start(1.0)
  480. meta:set_int("active", 1)
  481. else
  482. -- Slow down timer during sleep periods to reduce load.
  483. minetest.get_node_timer(pos):start(math_random(1, 3*60))
  484. meta:set_int("active", 0)
  485. end
  486. -- Update infotext.
  487. meta:set_string("infotext", leecher.compose_infotext(pos))
  488. meta:set_string("formspec", leecher.compose_formspec(pos))
  489. end
  490. leecher.on_construct =
  491. function(pos)
  492. leecher.data[minetest.hash_node_position(pos)] = nil
  493. end
  494. leecher.after_place_node =
  495. function(pos, placer, itemstack, pointed_thing)
  496. local meta = minetest.get_meta(pos)
  497. local node = minetest.get_node(pos)
  498. local owner = placer:get_player_name()
  499. local inv = meta:get_inventory()
  500. meta:set_string("owner", owner)
  501. meta:set_string("nodename", node.name)
  502. inv:set_size("buffer", 1)
  503. inv:set_size("main", 9)
  504. net2.clear_caches(pos, owner, "hv")
  505. meta:set_string("formspec", leecher.compose_formspec(pos))
  506. meta:set_string("infotext", leecher.compose_infotext(pos))
  507. nodestore.add_node(pos)
  508. local timer = minetest.get_node_timer(pos)
  509. timer:start(1.0)
  510. end
  511. leecher.on_blast =
  512. function(pos)
  513. local drops = {}
  514. drops[#drops+1] = "leecher:leecher"
  515. default.get_inventory_drops(pos, "main", drops)
  516. minetest.remove_node(pos)
  517. leecher.data[minetest.hash_node_position(pos)] = nil
  518. return drops
  519. end
  520. leecher.allow_metadata_inventory_put =
  521. function(pos, listname, index, stack, player)
  522. local pname = player:get_player_name()
  523. if minetest.test_protection(pos, pname) then
  524. return 0
  525. end
  526. -- Debug code.
  527. if minetest.is_singleplayer() then
  528. if stack:get_name() == "atomic:energy" and listname == "buffer" then
  529. return stack:get_count()
  530. end
  531. end
  532. return 0
  533. end
  534. leecher.allow_metadata_inventory_move =
  535. function(pos, from_list, from_index, to_list, to_index, count, player)
  536. return 0
  537. end
  538. leecher.allow_metadata_inventory_take =
  539. function(pos, listname, index, stack, player)
  540. local pname = player:get_player_name()
  541. if minetest.test_protection(pos, pname) then
  542. return 0
  543. end
  544. if listname == "main" then
  545. return stack:get_count()
  546. end
  547. return 0
  548. end
  549. leecher.on_metadata_inventory_move =
  550. function(pos)
  551. leecher.trigger_update(pos)
  552. end
  553. leecher.on_metadata_inventory_put =
  554. function(pos)
  555. leecher.trigger_update(pos)
  556. end
  557. leecher.on_metadata_inventory_take =
  558. function(pos, listname, index, stack, player)
  559. leecher.trigger_update(pos)
  560. end
  561. leecher.on_destruct =
  562. function(pos)
  563. local meta = minetest.get_meta(pos)
  564. net2.clear_caches(pos, meta:get_string("owner"), "hv")
  565. nodestore.del_node(pos)
  566. leecher.data[minetest.hash_node_position(pos)] = nil
  567. end
  568. leecher.on_place = function(itemstack, placer, pt)
  569. if pt.type == "node" then
  570. if city_block:in_no_leecher_zone(pt.under) then
  571. local pname = placer:get_player_name()
  572. minetest.chat_send_player(pname, "# Server: Mineral extraction is forbidden this close to a residential area!")
  573. return itemstack
  574. end
  575. end
  576. return minetest.item_place(itemstack, placer, pt)
  577. end
  578. if not leecher.run_once then
  579. minetest.register_node(":leecher:leecher", {
  580. description = "HV Mineral Outsalter\n\nThis leeches trace ores from rock above, very slowly.\nMust be placed in a water pool (wider is better).",
  581. tiles = {
  582. "technic_carbon_steel_block.png^default_tool_mesepick.png",
  583. "technic_carbon_steel_block.png",
  584. "technic_carbon_steel_block.png",
  585. "technic_carbon_steel_block.png",
  586. "technic_carbon_steel_block.png",
  587. "technic_carbon_steel_block.png",
  588. },
  589. groups = utility.dig_groups("machine"),
  590. paramtype2 = "facedir",
  591. is_ground_content = false,
  592. sounds = default.node_sound_metal_defaults(),
  593. drop = "leecher:leecher",
  594. on_rotate = function(...)
  595. return screwdriver.rotate_simple(...) end,
  596. allow_metadata_inventory_put = function(...)
  597. return leecher.allow_metadata_inventory_put(...) end,
  598. allow_metadata_inventory_move = function(...)
  599. return leecher.allow_metadata_inventory_move(...) end,
  600. allow_metadata_inventory_take = function(...)
  601. return leecher.allow_metadata_inventory_take(...) end,
  602. on_metadata_inventory_move = function(...)
  603. return leecher.on_metadata_inventory_move(...) end,
  604. on_metadata_inventory_put = function(...)
  605. return leecher.on_metadata_inventory_put(...) end,
  606. on_metadata_inventory_take = function(...)
  607. return leecher.on_metadata_inventory_take(...) end,
  608. on_punch = function(...)
  609. return leecher.on_punch(...) end,
  610. can_dig = function(...)
  611. return leecher.can_dig(...) end,
  612. on_timer = function(...)
  613. return leecher.on_timer(...) end,
  614. on_construct = function(...)
  615. return leecher.on_construct(...) end,
  616. on_destruct = function(...)
  617. return leecher.on_destruct(...) end,
  618. on_blast = function(...)
  619. return leecher.on_blast(...) end,
  620. after_place_node = function(...)
  621. return leecher.after_place_node(...) end,
  622. on_receive_fields = function(...)
  623. return leecher.on_receive_fields(...) end,
  624. on_place = function(...)
  625. return leecher.on_place(...) end,
  626. })
  627. minetest.register_craft({
  628. output = "leecher:leecher",
  629. recipe = {
  630. {"techcrafts:carbon_plate", "stack_filter:filter", "techcrafts:composite_plate"},
  631. {"techcrafts:electric_motor", "techcrafts:machine_casing", "gem_cutter:blade"},
  632. {"carbon_steel:block", "cb2:hv", "carbon_steel:block"}},
  633. })
  634. local c = "leecher:core"
  635. local f = leecher.modpath .. "/leecher.lua"
  636. reload.register_file(c, f, false)
  637. leecher.run_once = true
  638. end