init.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. local modpath = minetest.get_modpath("machines")
  2. dofile(modpath.."/hopper.lua")
  3. dofile(modpath.."/autofurnace.lua")
  4. local function splitname(name)
  5. local c = string.find(name, ":", 1)
  6. return string.sub(name, 1, c - 1), string.sub(name, c + 1, string.len(name))
  7. end
  8. local function grab_fuel(inv)
  9. local list = inv:get_list("fuel")
  10. for i,st in ipairs(list) do
  11. print(st:get_name())
  12. local fuel, remains
  13. fuel, remains = minetest.get_craft_result({
  14. method = "fuel",
  15. width = 1,
  16. items = {
  17. ItemStack(st:get_name())
  18. },
  19. })
  20. if fuel.time > 0 then
  21. -- Take fuel from fuel list
  22. st:take_item()
  23. inv:set_stack("fuel", i, st)
  24. return fuel.time
  25. end
  26. end
  27. return 0 -- no fuel found
  28. end
  29. local function can_dig(pos, player)
  30. local meta = minetest.get_meta(pos);
  31. local inv = meta:get_inventory()
  32. return inv:is_empty("fuel")
  33. end
  34. local function allow_metadata_inventory_put(pos, listname, index, stack, player)
  35. -- if minetest.is_protected(pos, player:get_player_name()) then
  36. -- return 0
  37. -- end
  38. -- local meta = minetest.get_meta(pos)
  39. -- local inv = meta:get_inventory()
  40. -- if listname == "fuel" then
  41. -- if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
  42. -- if inv:is_empty("src") then
  43. -- meta:set_string("infotext", "Furnace is empty")
  44. -- end
  45. -- return stack:get_count()
  46. -- else
  47. -- return 0
  48. -- end
  49. -- elseif listname == "src" then
  50. -- return stack:get_count()
  51. -- elseif listname == "dst" then
  52. -- return 0
  53. -- end
  54. return stack:get_count()
  55. end
  56. local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
  57. local meta = minetest.get_meta(pos)
  58. local inv = meta:get_inventory()
  59. local stack = inv:get_stack(from_list, from_index)
  60. return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
  61. end
  62. local function allow_metadata_inventory_take(pos, listname, index, stack, player)
  63. if minetest.is_protected(pos, player:get_player_name()) then
  64. return 0
  65. end
  66. return stack:get_count()
  67. end
  68. local function swap_node(pos, name)
  69. local node = minetest.get_node(pos)
  70. if node.name == name then
  71. return
  72. end
  73. node.name = name
  74. minetest.swap_node(pos, node)
  75. end
  76. -- local prominent_items = {
  77. -- ["default:cobble"] = 1
  78. -- ["default:wood"] = 1
  79. -- ["default:stick"] = 1
  80. -- ["tnt:gunpowder"] = 1
  81. -- ["tnt:tnt"] = 1
  82. --
  83. -- }
  84. local standard_recipies = {}
  85. -- find all the default items and cache their node names
  86. local default_items = {}
  87. for n,item in pairs(minetest.registered_items) do
  88. --print("name[ " .. n)
  89. if string.find(n, ":", 1) then
  90. m,p = splitname(n)
  91. if m == "default" then
  92. default_items[n] = p
  93. end
  94. end
  95. end
  96. local function get_canonical_item_for_group(group)
  97. for name,_ in pairs(default_items) do
  98. local item = minetest.registered_items[name]
  99. if item.groups[group] ~= nil then
  100. return name
  101. end
  102. end
  103. -- nothing in default, search further
  104. for name,item in pairs(minetest.registered_items) do
  105. if item.groups[group] ~= nil then
  106. return name
  107. end
  108. end
  109. return ""
  110. end
  111. local function set_craft_recipe(pos, index)
  112. local meta = minetest.get_meta(pos)
  113. local inv = meta:get_inventory()
  114. local protolist = inv:get_stack("proto", 1)
  115. if protolist == nil then
  116. -- no prototype
  117. return
  118. end
  119. --print(protolist:get_name())
  120. local recipes_all = minetest.get_all_craft_recipes(protolist:get_name())
  121. local recipes = {}
  122. for _,r in ipairs(recipes_all) do
  123. if r.type ~= "cooking" then
  124. table.insert(recipes, r)
  125. end
  126. end
  127. if #recipes == 0 then
  128. -- nothing to do
  129. return
  130. end
  131. local n = meta:get_float("n") or 1
  132. if n > #recipes or n <= 0 then
  133. n = (n % #recipes) + 1
  134. meta:set_float("n", n)
  135. end
  136. local recip = recipes[n]
  137. -- if 1 == 1 then return end
  138. local needed = {} -- specific items needed
  139. local needed_show = {} -- representative items to displat to the user
  140. local needed_groups = {} -- group items needed
  141. --print("---")
  142. for i,item in ipairs(recip.items) do
  143. --print("item - "..item)
  144. local name = item
  145. if item:sub(1, 6) == "group:" then
  146. local group = item:sub(7)
  147. needed_groups[group] = (needed_groups[group] or 0) + 1
  148. name = get_canonical_item_for_group(group)
  149. else
  150. needed[item] = (needed[item] or 0) + 1
  151. end
  152. needed_show[name] = (needed_show[name] or 0) + 1
  153. end
  154. --print("^^^")
  155. -- show needed item to the user
  156. local i = 1
  157. for name,qty in pairs(needed_show) do
  158. --print("needed name: " .. name)
  159. inv:set_stack("needs", i, name .. " " .. qty)
  160. i = i + 1
  161. end
  162. -- clear out the unused slots
  163. while i <= 9 do
  164. inv:set_stack("needs", i, "")
  165. i = i + 1
  166. end
  167. meta:set_string("work", minetest.serialize({
  168. needed = needed,
  169. needed_groups = needed_groups,
  170. recipe = recip,
  171. result = protolist:get_name(),
  172. }))
  173. end
  174. local function fancy_machine_node_timer(pos, elapsed)
  175. local meta = minetest.get_meta(pos)
  176. local fuel_time = meta:get_float("fuel_time") or 0
  177. local fuel_burned = meta:get_float("fuel_burned") or 0
  178. local make_time = meta:get_float("make_time") or 0
  179. local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
  180. local inv = meta:get_inventory()
  181. local burned = elapsed
  182. local turn_off = false
  183. local protolist = inv:get_stack("proto", 1)
  184. -- check fuel
  185. if fuel_time > 0 and fuel_burned + elapsed < fuel_time then
  186. fuel_burned = fuel_burned + elapsed
  187. meta:set_float("fuel_burned", fuel_burned + elapsed)
  188. else
  189. local t = grab_fuel(inv)
  190. if t <= 0 then -- out of fuel
  191. --print("out of fuel")
  192. meta:set_float("fuel_time", 0)
  193. meta:set_float("fuel_burned", 0)
  194. burned = fuel_time - fuel_burned
  195. print("no fuel")
  196. turn_off = true
  197. else
  198. -- roll into the next period
  199. fuel_burned = elapsed - (fuel_time - fuel_burned)
  200. fuel_time = t
  201. --print("fuel remaining: " .. (fuel_time - fuel_burned))
  202. meta:set_float("fuel_time", fuel_time)
  203. meta:set_float("fuel_burned", fuel_burned)
  204. end
  205. end
  206. -- todo: move to a better spot
  207. if turn_off then
  208. swap_node(pos, "machines:machine")
  209. return
  210. end
  211. proto = protolist:get_name()
  212. if proto == nil then
  213. return
  214. end
  215. local tmp = meta:get_string("work")
  216. if not tmp then
  217. print("no prototype selected")
  218. -- no prototype or recipe selected
  219. return
  220. end
  221. local work = minetest.deserialize(tmp)
  222. if not work or not work.needed then
  223. -- missing or bad data
  224. print("missing work data")
  225. return
  226. end
  227. local needed = work.needed
  228. local needed_groups = work.needed_groups
  229. -- collect all the ingredients
  230. -- todo: cache hopper locations
  231. -- look for hoppers
  232. local hoppers = minetest.find_nodes_in_area(
  233. {x=pos.x - 1, y=pos.y + 1, z = pos.z - 1},
  234. {x=pos.x + 1, y=pos.y + 1, z = pos.z + 1},
  235. "machines:hopper"
  236. )
  237. print(dump(needed_groups))
  238. local has_cnt = 0
  239. local has = {}
  240. local to_take = {}
  241. print("hoppers found: "..#hoppers)
  242. for _,hop in ipairs(hoppers) do
  243. local hmeta = minetest.get_meta(hop)
  244. local hinv = hmeta:get_inventory()
  245. local hlist = hinv:get_list("main")
  246. if #hlist > 0 then
  247. -- bug - needs to search for first item
  248. local hitem = hlist[1]
  249. if hitem then
  250. local hitem_name = hitem:get_name()
  251. print("hopper item: ".. hitem_name)
  252. if needed[hitem_name] then
  253. print("needed item")
  254. has[hitem_name] = (has[hitem_name] or 0) + hitem:get_count()
  255. to_take[hitem_name] = (to_take[hitem_name] or 0) + needed[hitem_name]
  256. else
  257. for g,cnt in pairs(needed_groups) do
  258. print("ng: ".. g)
  259. if minetest.registered_items[hitem_name].groups[g] ~= nil then
  260. print("found needed group item")
  261. has[hitem_name] = (has[hitem_name] or 0) + hitem:get_count()
  262. to_take[hitem_name] = (to_take[hitem_name] or 0) + cnt
  263. end
  264. end
  265. end
  266. end
  267. else
  268. print("hopper empty")
  269. end
  270. end
  271. local some_missing = 0
  272. for name,qty in pairs(needed) do
  273. if not has[name] or has[name] < qty then
  274. print("item: "..name.. " has: " .. (has[name] or "none") .. " need: " ..qty)
  275. some_missing = 1
  276. break
  277. end
  278. end
  279. if some_missing == 1 then
  280. print("not enough supply for machine at "..pos.x..","..pos.y..","..pos.z)
  281. swap_node(pos, "machines:machine")
  282. return false
  283. end
  284. print(dump(to_take))
  285. -- take the inputs to craft an item
  286. for _,hop in ipairs(hoppers) do
  287. local hmeta = minetest.get_meta(hop)
  288. local hinv = hmeta:get_inventory()
  289. --local hlist = hinv:get_list("main")
  290. for name,qty in pairs(to_take) do
  291. local taken = hinv:remove_item("main", name .. " " .. qty)
  292. print("took ".. taken:get_count() .. " of " .. taken:get_name())
  293. to_take[name] = qty - taken:get_count()
  294. if to_take[name] == 0 then
  295. to_take[name] = nil
  296. end
  297. end
  298. end
  299. -- recheck needed to make sure enough was taken
  300. -- output the item
  301. local outmeta = minetest.get_meta({x=pos.x, y=pos.y - 1, z=pos.z})
  302. local outinv = outmeta:get_inventory()
  303. local rem = outinv:add_item("main", work.recipe.output)
  304. if rem and rem:get_count() > 0 then
  305. swap_node(pos, "machines:machine")
  306. return false
  307. end
  308. return true
  309. end
  310. local function machine_node_timer(pos, elapsed)
  311. print("machine timer " .. elapsed)
  312. return fancy_machine_node_timer(pos, elapsed)
  313. end
  314. function get_machine_inactive_formspec(j)
  315. local i = j or 0
  316. return "size[8,8.5]"..
  317. default.gui_bg..
  318. default.gui_bg_img..
  319. default.gui_slots..
  320. -- "list[context;src;2.75,0.5;1,1;]"..
  321. "list[context;needs;0,0;8,2;]"..
  322. "list[context;proto;1.5,1;1,1;]"..
  323. "list[context;fuel;4,2;3,2;]"..
  324. "image[2,2.5;1,1;default_furnace_fire_bg.png]"..
  325. -- "image[3.5,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
  326. "image_button[0.5,1.25;0.8,0.8;creative_prev_icon.png;recipe_prev;]"..
  327. "image_button[2.5,1.25;0.8,0.8;creative_next_icon.png;recipe_next;]"..
  328. -- "list[context;dst;4.75,0.96;2,2;]"..
  329. "list[current_player;main;0,4.25;8,1;]"..
  330. "list[current_player;main;0,5.5;8,3;8]"..
  331. "listring[context;dst]"..
  332. "listring[current_player;main]"..
  333. "listring[context;src]"..
  334. "listring[current_player;main]"..
  335. "listring[context;fuel]"..
  336. "listring[current_player;main]"..
  337. default.get_hotbar_bg(0, 4.25)
  338. end
  339. function get_machine_active_formspec(fuel_pct)
  340. return "size[8,8.5]"..
  341. default.gui_bg..
  342. default.gui_bg_img..
  343. default.gui_slots..
  344. -- "list[context;src;2.75,0.5;1,1;]"..
  345. "list[context;needs;0,0;8,2;]"..
  346. "list[context;proto;1.5,1;1,1;]"..
  347. "list[context;fuel;4,2;3,2;]"..
  348. "image[2,2.5;1,1;default_furnace_fire_bg.png]^[lowpart:"..
  349. (100-fuel_pct)..":default_furnace_fire_fg.png]"..
  350. -- "image[3.5,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
  351. "image_button[0.5,1.25;0.8,0.8;creative_prev_icon.png;recipe_prev;]"..
  352. "image_button[2.5,1.25;0.8,0.8;creative_next_icon.png;recipe_next;]"..
  353. -- "list[context;dst;4.75,0.96;2,2;]"..
  354. "list[current_player;main;0,4.25;8,1;]"..
  355. "list[current_player;main;0,5.5;8,3;8]"..
  356. "listring[context;dst]"..
  357. "listring[current_player;main]"..
  358. "listring[context;src]"..
  359. "listring[current_player;main]"..
  360. "listring[context;fuel]"..
  361. "listring[current_player;main]"..
  362. default.get_hotbar_bg(0, 4.25)
  363. end
  364. minetest.register_node("machines:machine_on", {
  365. description = "Machine",
  366. tiles = {
  367. "default_tin_block.png", "default_tin_block.png",
  368. "default_bronze_block.png", "default_tin_block.png",
  369. "default_tin_block.png",
  370. {
  371. image = "default_furnace_front_active.png",
  372. backface_culling = false,
  373. animation = {
  374. type = "vertical_frames",
  375. aspect_w = 16,
  376. aspect_h = 16,
  377. length = 1.5
  378. },
  379. }
  380. },
  381. paramtype2 = "facedir",
  382. groups = {cracky=2},
  383. legacy_facedir_simple = true,
  384. is_ground_content = false,
  385. sounds = default.node_sound_stone_defaults(),
  386. stack_max = 1,
  387. can_dig = can_dig,
  388. on_timer = machine_node_timer,
  389. on_punch = function(pos)
  390. swap_node(pos, "machines:machine") -- regular is the off version
  391. end,
  392. on_construct = function(pos)
  393. local meta = minetest.get_meta(pos)
  394. meta:set_string("formspec", get_machine_active_formspec())
  395. local inv = meta:get_inventory()
  396. inv:set_size('needs', 8)
  397. inv:set_size('fuel', 6)
  398. inv:set_size('proto', 1)
  399. print("constructed")
  400. minetest.get_node_timer(pos):start(1.0)
  401. end,
  402. on_metadata_inventory_move = function(pos)
  403. -- on_metadata_inventory_put = function(pos, listname)
  404. --if listname == "proto" then
  405. -- set_craft_recipe(pos, 1)
  406. --end
  407. end,
  408. on_metadata_inventory_put = function(pos, listname)
  409. if listname == "proto" then
  410. set_craft_recipe(pos, 1)
  411. end
  412. end,
  413. -- on_blast = function(pos)
  414. -- local drops = {}
  415. -- default.get_inventory_drops(pos, "src", drops)
  416. -- default.get_inventory_drops(pos, "fuel", drops)
  417. -- default.get_inventory_drops(pos, "dst", drops)
  418. -- drops[#drops+1] = "machines:machine"
  419. -- minetest.remove_node(pos)
  420. -- return drops
  421. -- end,
  422. allow_metadata_inventory_put = allow_metadata_inventory_put,
  423. allow_metadata_inventory_move = allow_metadata_inventory_move,
  424. allow_metadata_inventory_take = allow_metadata_inventory_take,
  425. on_receive_fields = function(pos, form, fields, sender)
  426. local meta = minetest.get_meta(pos)
  427. local i = meta:get_float("n") or 1
  428. if fields.recipe_next then
  429. i = i + 1
  430. elseif fields.recipe_prev then
  431. i = i - 1
  432. end
  433. meta:set_float("n", i)
  434. set_craft_recipe(pos, i)
  435. meta:set_string("formspec", get_machine_inactive_formspec(i))
  436. end
  437. })
  438. minetest.register_node("machines:machine", {
  439. description = "Machine",
  440. tiles = {
  441. "default_furnace_top.png", "default_furnace_bottom.png",
  442. "default_bronze_block.png", "default_furnace_side.png",
  443. "default_furnace_side.png", "default_furnace_front.png"
  444. },
  445. paramtype2 = "facedir",
  446. groups = {cracky=2},
  447. legacy_facedir_simple = true,
  448. is_ground_content = false,
  449. sounds = default.node_sound_stone_defaults(),
  450. stack_max = 1,
  451. can_dig = can_dig,
  452. on_punch = function(pos)
  453. swap_node(pos, "machines:machine_on")
  454. minetest.get_node_timer(pos):start(1.0)
  455. end,
  456. on_construct = function(pos)
  457. local meta = minetest.get_meta(pos)
  458. meta:set_string("formspec", get_machine_inactive_formspec())
  459. local inv = meta:get_inventory()
  460. inv:set_size('needs', 8)
  461. inv:set_size('fuel', 6)
  462. inv:set_size('proto', 1)
  463. -- minetest.get_node_timer(pos):start(1.0)
  464. end,
  465. on_metadata_inventory_move = function(pos)
  466. -- minetest.get_node_timer(pos):start(1.0)
  467. end,
  468. on_metadata_inventory_put = function(pos, listname)
  469. if listname == "proto" then
  470. set_craft_recipe(pos, 1)
  471. end
  472. end,
  473. -- on_blast = function(pos)
  474. -- local drops = {}
  475. -- default.get_inventory_drops(pos, "src", drops)
  476. -- default.get_inventory_drops(pos, "fuel", drops)
  477. -- default.get_inventory_drops(pos, "dst", drops)
  478. -- drops[#drops+1] = "machines:machine"
  479. -- minetest.remove_node(pos)
  480. -- return drops
  481. -- end,
  482. allow_metadata_inventory_put = allow_metadata_inventory_put,
  483. allow_metadata_inventory_move = allow_metadata_inventory_move,
  484. allow_metadata_inventory_take = allow_metadata_inventory_take,
  485. on_receive_fields = function(pos, form, fields, sender)
  486. local meta = minetest.get_meta(pos)
  487. local i = meta:get_float("n") or 1
  488. if fields.recipe_next then
  489. i = i + 1
  490. elseif fields.recipe_prev then
  491. i = i - 1
  492. end
  493. meta:set_float("n", i)
  494. set_craft_recipe(pos, i)
  495. meta:set_string("formspec", get_machine_inactive_formspec(i))
  496. end
  497. })
  498. minetest.register_craft({
  499. output = 'machines:machine',
  500. recipe = {
  501. {'default:bronze_ingot', 'default:bronze_ingot', 'default:bronze_ingot'},
  502. {'default:bronze_ingot', 'default:mese_crystal', 'default:bronze_ingot'},
  503. {'default:bronze_ingot', 'default:bronze_ingot', 'default:bronze_ingot'},
  504. }
  505. })