v2.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. if not minetest.global_exists("grind2") then grind2 = {} end
  2. grind2.modpath = minetest.get_modpath("grinder")
  3. local MACHINE_NAME = "Grinder"
  4. local MACHINE_DESC = "This grinds things.\nCan burn mese for fuel when off-grid.\nAlternatively, connect to a power-network."
  5. local MACHINE_FUEL_EU_PER_SEC = 80
  6. local RECIPE_TYPE = "grinding"
  7. if not minetest.global_exists("grind2_lv") then grind2_lv = {} end
  8. if not minetest.global_exists("grind2_mv") then grind2_mv = {} end
  9. -- Localize for performance.
  10. local math_floor = math.floor
  11. local math_random = math.random
  12. for j, t in ipairs({
  13. {tier="lv", up="LV", speed=2.0, level=1, buffer=1000, demand=200},
  14. {tier="mv", up="MV", speed=0.6, level=2, buffer=6000, demand=300},
  15. }) do
  16. -- Which function table are we operating on?
  17. local func = _G["grind2_" .. t.tier]
  18. -- Read values from balancing table.
  19. local techname = "grinder_" .. t.tier
  20. t.speed = tech[techname].timecut
  21. t.buffer = tech[techname].buffer
  22. t.demand = tech[techname].demand
  23. func.get_formspec_defaults = function()
  24. local str =
  25. default.gui_bg ..
  26. default.gui_bg_img ..
  27. default.gui_slots
  28. return str
  29. end
  30. func.formspec_active = function(fuel_percent, item_percent)
  31. local x1 = 3.5
  32. local x2 = 4.5
  33. local x3 = 5.5
  34. local x4 = 0.5
  35. if t.level <= 1 then
  36. x1 = 3
  37. x2 = 4
  38. x3 = 5
  39. x4 = 1
  40. end
  41. local formspec =
  42. "size[8,8.5]" ..
  43. func.get_formspec_defaults() ..
  44. "label[" .. x1 .. ",0;Fuel & Input]" ..
  45. "list[context;src;" .. x1 .. ",0.5;1,1;]" ..
  46. "list[context;fuel;" .. x1 .. ",2.5;1,1;]" ..
  47. utility.progress_image(x1, 1.5, "machine_progress_bg.png", "machine_progress_fg.png", fuel_percent) ..
  48. utility.progress_image(x2, 1.5, "gui_furnace_arrow_bg.png", "gui_furnace_arrow_fg.png", item_percent, "^[transformR270") ..
  49. "label[" .. x3 .. ",0.46;Destination]" ..
  50. "list[context;dst;" .. x3 .. ",0.96;2,2;]" ..
  51. "list[current_player;main;0,4.25;8,1;]" ..
  52. "list[current_player;main;0,5.5;8,3;8]"
  53. if t.level > 1 then
  54. formspec = formspec ..
  55. "label[0.5,0;Upgrades]" ..
  56. "list[context;upg;0.5,0.5;2,1;]"
  57. end
  58. formspec = formspec ..
  59. "label[" .. x4 .. ",2;Buffer]" ..
  60. "list[context;buffer;" .. x4 .. ",2.5;1,1;]" ..
  61. "listring[context;dst]" ..
  62. "listring[current_player;main]" ..
  63. "listring[context;src]" ..
  64. "listring[current_player;main]" ..
  65. "listring[context;fuel]"..
  66. "listring[current_player;main]"..
  67. default.get_hotbar_bg(0, 4.25)
  68. return formspec
  69. end
  70. func.formspec_inactive = function()
  71. return func.formspec_active(0, 0)
  72. end
  73. func.on_punch =
  74. function(pos, node, puncher, pointed_thing)
  75. func.trigger_update(pos)
  76. end
  77. func.trigger_update =
  78. function(pos)
  79. local timer = minetest.get_node_timer(pos)
  80. -- Start timer even if already running.
  81. timer:start(1.0)
  82. end
  83. func.can_dig = function(pos, player)
  84. local meta = minetest.get_meta(pos)
  85. local inv = meta:get_inventory()
  86. if t.level > 1 then
  87. return inv:is_empty("fuel") and
  88. inv:is_empty("dst") and
  89. inv:is_empty("src") and
  90. inv:is_empty("upg")
  91. else
  92. return inv:is_empty("fuel") and
  93. inv:is_empty("dst") and
  94. inv:is_empty("src")
  95. -- No upgrade slots.
  96. end
  97. end
  98. func.get_speed =
  99. function(pos, meta, inv)
  100. if t.level <= 1 then
  101. return t.speed
  102. end
  103. local clus = utility.inventory_count_items(inv, "upg", "techcrafts:control_logic_unit")
  104. if clus == 1 then
  105. return (t.speed*0.8)
  106. elseif clus == 2 then
  107. return (t.speed*0.7)
  108. end
  109. return t.speed
  110. end
  111. func.get_demand =
  112. function(pos, meta, inv)
  113. if t.level <= 1 then
  114. return t.demand
  115. end
  116. local bats = utility.inventory_count_items(inv, "upg", "battery:battery")
  117. if bats == 1 then
  118. return math_floor(t.demand*0.8)
  119. elseif bats == 2 then
  120. return math_floor(t.demand*0.7)
  121. end
  122. return t.demand
  123. end
  124. func.has_public_access =
  125. function(pos)
  126. if t.level <= 1 then
  127. return
  128. end
  129. local meta = minetest.get_meta(pos)
  130. local inv = meta:get_inventory()
  131. local s1 = inv:get_stack("upg", 1)
  132. if s1:get_count() > 0 then
  133. if minetest.get_item_group(s1:get_name(), "chest") > 0 then
  134. return true
  135. end
  136. end
  137. local s2 = inv:get_stack("upg", 2)
  138. if s2:get_count() > 0 then
  139. if minetest.get_item_group(s2:get_name(), "chest") > 0 then
  140. return true
  141. end
  142. end
  143. end
  144. func.allow_metadata_inventory_put =
  145. function(pos, listname, index, stack, player)
  146. local pname = player:get_player_name()
  147. local public = func.has_public_access(pos)
  148. local protected = false
  149. if minetest.test_protection(pos, pname) then
  150. protected = true
  151. end
  152. if listname == "fuel" and (not protected or public) then
  153. if minetest.get_craft_result({method="mesefuel", width=1, items={stack}}).time ~= 0 then
  154. return stack:get_count()
  155. end
  156. elseif listname == "src" and (not protected or public)then
  157. return stack:get_count()
  158. elseif t.level > 1 and listname == "upg" and not protected then
  159. if minetest.get_item_group(stack:get_name(), "chest") > 0 then
  160. return stack:get_count()
  161. elseif stack:get_name() == "battery:battery" then
  162. return stack:get_count()
  163. elseif stack:get_name() == "techcrafts:control_logic_unit" then
  164. return stack:get_count()
  165. end
  166. -- Enables testing.
  167. --elseif listname == "buffer" and stack:get_name() == "atomic:energy" then
  168. -- return stack:get_count()
  169. end
  170. return 0
  171. end
  172. func.allow_metadata_inventory_move =
  173. function(pos, from_list, from_index, to_list, to_index, count, player)
  174. local pname = player:get_player_name()
  175. local public = func.has_public_access(pos)
  176. local protected = false
  177. if minetest.test_protection(pos, pname) then
  178. protected = true
  179. end
  180. if (from_list == "upg" or to_list == "upg") and protected and t.level > 1 then
  181. return 0
  182. end
  183. if not protected or public then
  184. if from_list == "buffer" or to_list == "buffer" then
  185. return 0
  186. end
  187. if from_list == to_list then
  188. return count
  189. end
  190. end
  191. return 0
  192. end
  193. func.allow_metadata_inventory_take =
  194. function(pos, listname, index, stack, player)
  195. local pname = player:get_player_name()
  196. local public = func.has_public_access(pos)
  197. local protected = false
  198. if minetest.test_protection(pos, pname) then
  199. protected = true
  200. end
  201. if listname == "fuel" and (not protected or public) then
  202. return stack:get_count()
  203. elseif listname == "dst" and (not protected or public) then
  204. return stack:get_count()
  205. elseif listname == "upg" and not protected and t.level > 1 then
  206. return stack:get_count()
  207. elseif listname == "src" and (not protected or public) then
  208. return stack:get_count()
  209. end
  210. return 0
  211. end
  212. func.on_timer = function(pos, elapsed)
  213. --[[
  214. 1) Check if we have cookable content:
  215. a) If YES, goto 'on cook content' (step 2).
  216. b) If NO, stop machine, end func.
  217. 2) On cook content:
  218. a) Check if item fully cooked (cooking time >= needed time).
  219. 1) If NO, goto 'check if we have buffered energy'.
  220. 2) If YES, put result into dest, decrement source, end func.
  221. a) Set machine active.
  222. b) Check if we have needed amount of buffered energy.
  223. 1) If YES, increment cooktime, consume energy, end func.
  224. a) Set machine active.
  225. 2) If NO, get energy (on get energy). Send wanted energy.
  226. 3) On get energy (buffer should be empty or nearly so):
  227. a) Check if we can get energy from fuel.
  228. 1) If YES, consume fuel to buffered energy, goto 'got enough energy?'
  229. 2) If NO, goto 'get energy from network'.
  230. b) Check if we can get energy from network.
  231. 1) If YES, get energy from network, goto 'got enough energy?'.
  232. 2) If NO (network exhausted/buffer full), stop machine, end func.
  233. 4) Did we get enough energy? (Received wanted energy from step 2b2.)
  234. a) If YES, end func.
  235. 1) Set machine active.
  236. b) If NO, goto 'on get energy' (trying again).
  237. 5) End func.
  238. a) Check if machine is active.
  239. 1) If YES, keep running.
  240. 2) If NO, enter sleep mode.
  241. --]]
  242. local meta = minetest.get_meta(pos)
  243. local inv = meta:get_inventory()
  244. local wantedenergy = 0
  245. local keeprunning = false
  246. local demand = func.get_demand(pos, meta, inv)
  247. local speed = func.get_speed(pos, meta, inv)
  248. -- Check if we have cookable content.
  249. local srclist = inv:get_list("src")
  250. local cookable = true
  251. local cooked, aftercooked = minetest.get_craft_result({
  252. method = RECIPE_TYPE, width = 1, items = srclist})
  253. if cooked.time == 0 then
  254. cookable = false
  255. end
  256. -- If we have cookable content, goto 'on cook content'.
  257. if cookable then
  258. -- If total energy time wasn't recorded yet, record it.
  259. if meta:get_int("fueltotaltime") == 0 then
  260. local energy = inv:get_stack("buffer", 1):get_count()
  261. meta:set_int("fueltotaltime", math_floor(energy/demand))
  262. end
  263. goto on_cook
  264. else
  265. -- Stop machine.
  266. keeprunning = false
  267. goto end_func
  268. end
  269. -- On cook content.
  270. ::on_cook::
  271. do
  272. -- Check if item is fully cooked.
  273. if meta:get_float("srctime") >= cooked.time then
  274. -- Place result in dst list if possible
  275. if inv:room_for_item("dst", cooked.item) then
  276. inv:add_item("dst", cooked.item)
  277. inv:set_stack("src", 1, aftercooked.items[1])
  278. meta:set_float("srctime", 0)
  279. -- Check if there are still items in the source slot.
  280. local srclist = inv:get_list("src")
  281. local cooked, aftercooked = minetest.get_craft_result({
  282. method = RECIPE_TYPE, width = 1, items = srclist})
  283. if cooked.time == 0 then
  284. -- No more items? Stop machine.
  285. meta:set_int("fueltotaltime", 0)
  286. keeprunning = false
  287. goto end_func
  288. else
  289. -- Set machine active.
  290. keeprunning = true
  291. goto end_func
  292. end
  293. else
  294. -- Stop machine, no room!
  295. meta:set_int("fueltotaltime", 0)
  296. keeprunning = false
  297. goto end_func
  298. end
  299. else
  300. -- Check if we have buffered energy.
  301. goto check_buffered_energy
  302. end
  303. end
  304. -- Check if we have buffered energy.
  305. ::check_buffered_energy::
  306. do
  307. local energy = inv:get_stack("buffer", 1)
  308. if energy:get_count() >= demand then
  309. -- Increment cooktime.
  310. meta:set_float("srctime", meta:get_float("srctime") + 1/speed)
  311. energy:take_item(math_floor(demand/speed))
  312. inv:set_stack("buffer", 1, energy)
  313. -- Set machine active.
  314. keeprunning = true
  315. goto end_func
  316. else
  317. -- Goto 'on get energy'. Send wanted amount of energy.
  318. wantedenergy = demand
  319. goto on_get_energy
  320. end
  321. end
  322. -- On get energy.
  323. ::on_get_energy::
  324. do
  325. -- Check if we can get energy from fuel.
  326. local fuellist = inv:get_list("fuel")
  327. local fuel, afterfuel = minetest.get_craft_result({
  328. method = "mesefuel", width = 1, items = fuellist})
  329. if fuel.time > 0 then
  330. local old = inv:get_stack("buffer", 1):get_count()
  331. local energy = old + math_floor(fuel.time * MACHINE_FUEL_EU_PER_SEC)
  332. meta:set_int("fueltotaltime", math_floor(energy/demand))
  333. inv:set_stack("buffer", 1, "atomic:energy " .. energy)
  334. inv:set_stack("fuel", 1, afterfuel.items[1])
  335. goto check_got_enough_energy
  336. else
  337. goto get_energy_from_network
  338. end
  339. end
  340. -- Check if we can get energy from network.
  341. ::get_energy_from_network::
  342. do
  343. local current = inv:get_stack("buffer", 1)
  344. if current:get_count() < t.buffer then
  345. local owner = meta:get_string("owner")
  346. local get_amount = t.buffer
  347. local energy = net2.get_energy(pos, owner, get_amount, t.tier)
  348. if energy > 0 then
  349. local old = inv:get_stack("buffer", 1):get_count()
  350. energy = energy + old
  351. meta:set_int("fueltotaltime", math_floor(energy/demand))
  352. inv:set_stack("buffer", 1, "atomic:energy " .. energy)
  353. goto check_got_enough_energy
  354. else
  355. -- Stop machine (network exhausted).
  356. keeprunning = false
  357. goto end_func
  358. end
  359. else
  360. -- Stop machine (buffer full).
  361. keeprunning = false
  362. goto end_func
  363. end
  364. end
  365. -- Check if we got enough energy.
  366. ::check_got_enough_energy::
  367. do
  368. local energy = inv:get_stack("buffer", 1)
  369. if energy:get_count() >= wantedenergy then
  370. keeprunning = true
  371. goto end_func
  372. else
  373. -- Keep trying to get energy.
  374. goto on_get_energy
  375. end
  376. end
  377. -- End func.
  378. ::end_func::
  379. do
  380. if keeprunning then
  381. local itempercent = 0
  382. if cookable then
  383. itempercent = math_floor(meta:get_float("srctime") / cooked.time * 100)
  384. end
  385. local fueltime = math_floor(inv:get_stack("buffer", 1):get_count()/demand)
  386. local fueltotaltime = meta:get_int("fueltotaltime")
  387. local fuelpercent = math_floor(fueltime / fueltotaltime * 100)
  388. local eu_demand = math_floor(demand/speed)
  389. local infotext = t.up .. " " .. MACHINE_NAME .. " (Active)\n" ..
  390. "Demand: " .. eu_demand .. " EU Per/Sec"
  391. local formspec = func.formspec_active(fuelpercent, itempercent)
  392. meta:set_string("infotext", infotext)
  393. meta:set_string("formspec", formspec)
  394. local swapped = machines.swap_node(pos, "grind2:" .. t.tier .. "_active")
  395. minetest.get_node_timer(pos):start(1.0)
  396. if swapped then
  397. ambiance.spawn_sound_beacon("ambiance:grinder_active", pos)
  398. end
  399. else
  400. local infotext = t.up .. " " .. MACHINE_NAME .. " (Standby)\n" ..
  401. "Demand: 0 EU Per/Sec"
  402. local formspec = func.formspec_inactive()
  403. meta:set_string("infotext", infotext)
  404. meta:set_string("formspec", formspec)
  405. meta:set_int("fueltotaltime", 0)
  406. meta:set_float("srctime", 0)
  407. machines.swap_node(pos, "grind2:" .. t.tier .. "_inactive")
  408. minetest.get_node_timer(pos):start(math_random(1, 3*60))
  409. end
  410. end
  411. end
  412. func.on_construct =
  413. function(pos)
  414. end
  415. func.on_destruct =
  416. function(pos)
  417. local meta = minetest.get_meta(pos)
  418. local owner = meta:get_string("owner")
  419. net2.clear_caches(pos, owner, t.tier)
  420. nodestore.del_node(pos)
  421. end
  422. func.after_place_node =
  423. function(pos, placer, itemstack, pointed_thing)
  424. local owner = placer:get_player_name()
  425. local meta = minetest.get_meta(pos)
  426. local node = minetest.get_node(pos)
  427. local inv = meta:get_inventory()
  428. meta:set_string("owner", owner)
  429. meta:set_string("nodename", node.name)
  430. inv:set_size('src', 1)
  431. inv:set_size('fuel', 1)
  432. inv:set_size('dst', 4)
  433. if t.level > 1 then
  434. inv:set_size('upg', 2)
  435. end
  436. inv:set_size('buffer', 1)
  437. meta:set_string("formspec", func.formspec_inactive())
  438. meta:set_string("infotext", t.up .. " " .. MACHINE_NAME .. " (Standby)\n" ..
  439. "Demand: 0 EU Per/Sec")
  440. nodestore.add_node(pos)
  441. net2.clear_caches(pos, owner, t.tier)
  442. local timer = minetest.get_node_timer(pos)
  443. timer:start(1.0)
  444. end
  445. func.on_metadata_inventory_move =
  446. function(pos)
  447. func.trigger_update(pos)
  448. end
  449. func.on_metadata_inventory_put =
  450. function(pos)
  451. func.trigger_update(pos)
  452. end
  453. func.on_metadata_inventory_take =
  454. function(pos)
  455. func.trigger_update(pos)
  456. end
  457. func.on_blast = function(pos)
  458. local drops = {}
  459. default.get_inventory_drops(pos, "src", drops)
  460. default.get_inventory_drops(pos, "fuel", drops)
  461. default.get_inventory_drops(pos, "dst", drops)
  462. if t.level > 1 then
  463. default.get_inventory_drops(pos, "upg", drops)
  464. end
  465. drops[#drops+1] = "grind2:" .. t.tier .. "_inactive"
  466. minetest.remove_node(pos)
  467. return drops
  468. end
  469. end
  470. if not grind2.run_once then
  471. for j, t in ipairs({
  472. {tier="lv", up="LV"},
  473. {tier="mv", up="MV"},
  474. }) do
  475. -- Which function table are we operating on?
  476. local func = _G["grind2_" .. t.tier]
  477. for k, v in ipairs({
  478. {name="inactive", light=0, tile="grinder_" .. t.tier .. "_front.png"},
  479. {name="active", light=8, tile="grinder_" .. t.tier .. "_front_active.png"},
  480. }) do
  481. minetest.register_node(":grind2:" .. t.tier .. "_" .. v.name, {
  482. description = t.up .. " " .. MACHINE_NAME .. "\n\n" .. MACHINE_DESC,
  483. tiles = {
  484. "grinder_" .. t.tier .. "_top.png", "grinder_" .. t.tier .. "_bottom.png",
  485. "grinder_" .. t.tier .. "_side.png", "grinder_" .. t.tier .. "_side.png",
  486. "grinder_" .. t.tier .. "_side.png", v.tile,
  487. },
  488. paramtype2 = "facedir",
  489. groups = utility.dig_groups("machine"),
  490. light_source = v.light,
  491. is_ground_content = false,
  492. sounds = default.node_sound_metal_defaults(),
  493. drop = "grind2:" .. t.tier .. "_inactive",
  494. on_rotate = function(...)
  495. return screwdriver.rotate_simple(...) end,
  496. can_dig = function(...)
  497. return func.can_dig(...) end,
  498. on_timer = function(...)
  499. return func.on_timer(...) end,
  500. on_construct = function(...)
  501. return func.on_construct(...) end,
  502. on_destruct = function(...)
  503. return func.on_destruct(...) end,
  504. on_blast = function(...)
  505. return func.on_blast(...) end,
  506. on_punch = function(...)
  507. return func.on_punch(...) end,
  508. after_place_node = function(...)
  509. return func.after_place_node(...) end,
  510. on_metadata_inventory_move = function(...)
  511. return func.on_metadata_inventory_move(...) end,
  512. on_metadata_inventory_put = function(...)
  513. return func.on_metadata_inventory_put(...) end,
  514. on_metadata_inventory_take = function(...)
  515. return func.on_metadata_inventory_take(...) end,
  516. allow_metadata_inventory_put = function(...)
  517. return func.allow_metadata_inventory_put(...) end,
  518. allow_metadata_inventory_move = function(...)
  519. return func.allow_metadata_inventory_move(...) end,
  520. allow_metadata_inventory_take = function(...)
  521. return func.allow_metadata_inventory_take(...) end,
  522. })
  523. end
  524. end
  525. minetest.register_alias("grind2:inactive", "grind2:mv_inactive")
  526. minetest.register_alias("grind2:active", "grind2:mv_active")
  527. local c = "grind2:core"
  528. local f = grind2.modpath .. "/v2.lua"
  529. reload.register_file(c, f, false)
  530. grind2.run_once = true
  531. end