leecher.lua 21 KB

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