furnace.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. --
  2. -- Formspecs
  3. --
  4. local function active_formspec(fuel_percent, item_percent)
  5. local formspec =
  6. "size[8,8.5]"..
  7. "list[current_name;src;2.5,3;1,1;]"..
  8. "list[current_name;fuel;3.5,1.5;1,1;]"..
  9. "image[3.5,0.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
  10. (100-fuel_percent)..":default_furnace_fire_fg.png]"..
  11. "image[3.5,3;1,1;gui_furnace_arrow_bg.png^[lowpart:"..
  12. (item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
  13. "list[current_name;dst;4.5,3;1,1;]"..
  14. "list[current_player;main;0,4.75;8,4;]"..
  15. "listring[current_name;dst]"..
  16. "listring[current_player;main]"..
  17. "listring[current_name;src]"..
  18. "listring[current_player;main]"..
  19. "listring[current_name;fuel]"..
  20. "listring[current_player;main]"
  21. return formspec
  22. end
  23. local inactive_formspec =
  24. "size[8,8.5]"..
  25. "list[current_name;src;2.5,3;1,1;]"..
  26. "list[current_name;fuel;3.5,1.5;1,1;]"..
  27. "image[3.5,0.5;1,1;default_furnace_fire_bg.png]"..
  28. "image[3.5,3;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
  29. "list[current_name;dst;4.5,3;1,1;]"..
  30. "list[current_player;main;0,4.75;8,4;]"..
  31. "listring[current_name;dst]"..
  32. "listring[current_player;main]"..
  33. "listring[current_name;src]"..
  34. "listring[current_player;main]"..
  35. "listring[current_name;fuel]"..
  36. "listring[current_player;main]"
  37. --
  38. -- Node callback functions that are the same for active and inactive furnace
  39. --
  40. local function can_dig(pos, player)
  41. local meta = minetest.get_meta(pos);
  42. local inv = meta:get_inventory()
  43. return inv:is_empty("fuel") and inv:is_empty("dst") and inv:is_empty("src")
  44. end
  45. local function allow_metadata_inventory_put(pos, listname, index, stack, player)
  46. if minetest.is_protected(pos, player:get_player_name()) then
  47. return 0
  48. end
  49. local meta = minetest.get_meta(pos)
  50. local inv = meta:get_inventory()
  51. if listname == "fuel" then
  52. if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
  53. if inv:is_empty("src") then
  54. meta:set_string("infotext", "Furnace is empty")
  55. end
  56. return stack:get_count()
  57. else
  58. return 0
  59. end
  60. elseif listname == "src" then
  61. return stack:get_count()
  62. elseif listname == "dst" then
  63. return 0
  64. end
  65. end
  66. local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
  67. local meta = minetest.get_meta(pos)
  68. local inv = meta:get_inventory()
  69. local stack = inv:get_stack(from_list, from_index)
  70. return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
  71. end
  72. local function allow_metadata_inventory_take(pos, listname, index, stack, player)
  73. if minetest.is_protected(pos, player:get_player_name()) then
  74. return 0
  75. end
  76. return stack:get_count()
  77. end
  78. local function swap_node(pos, name)
  79. local node = minetest.get_node(pos)
  80. if node.name == name then
  81. return
  82. end
  83. node.name = name
  84. minetest.swap_node(pos, node)
  85. end
  86. local function furnace_node_timer(pos, elapsed)
  87. -- Inizialize metadata
  88. local meta = minetest.get_meta(pos)
  89. local fuel_time = meta:get_float("fuel_time") or 0
  90. local src_time = meta:get_float("src_time") or 0
  91. local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
  92. local inv = meta:get_inventory()
  93. local srclist, fuellist
  94. local cookable, cooked
  95. local fuel
  96. local update = true
  97. while elapsed > 0 and update do
  98. update = false
  99. srclist = inv:get_list("src")
  100. fuellist = inv:get_list("fuel")
  101. -- Cooking
  102. -- Check if we have cookable content
  103. local aftercooked
  104. cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
  105. cookable = cooked.time ~= 0
  106. local el = math.min(elapsed, fuel_totaltime - fuel_time)
  107. if cookable then -- fuel lasts long enough, adjust el to cooking duration
  108. el = math.min(el, cooked.time - src_time)
  109. end
  110. -- Check if we have enough fuel to burn
  111. if fuel_time < fuel_totaltime then
  112. -- The furnace is currently active and has enough fuel
  113. fuel_time = fuel_time + el
  114. -- If there is a cookable item then check if it is ready yet
  115. if cookable then
  116. src_time = src_time + el
  117. if src_time >= cooked.time then
  118. -- Place result in dst list if possible
  119. if inv:room_for_item("dst", cooked.item) then
  120. inv:add_item("dst", cooked.item)
  121. inv:set_stack("src", 1, aftercooked.items[1])
  122. src_time = src_time - cooked.time
  123. update = true
  124. end
  125. else
  126. -- Item could not be cooked: probably missing fuel
  127. update = true
  128. end
  129. end
  130. else
  131. -- Furnace ran out of fuel
  132. if cookable then
  133. -- We need to get new fuel
  134. local afterfuel
  135. fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
  136. if fuel.time == 0 then
  137. -- No valid fuel in fuel list
  138. fuel_totaltime = 0
  139. src_time = 0
  140. else
  141. -- Take fuel from fuel list
  142. inv:set_stack("fuel", 1, afterfuel.items[1])
  143. update = true
  144. fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time)
  145. end
  146. else
  147. -- We don't need to get new fuel since there is no cookable item
  148. fuel_totaltime = 0
  149. src_time = 0
  150. end
  151. fuel_time = 0
  152. end
  153. elapsed = elapsed - el
  154. end
  155. if fuel and fuel_totaltime > fuel.time then
  156. fuel_totaltime = fuel.time
  157. end
  158. if srclist[1]:is_empty() then
  159. src_time = 0
  160. end
  161. --
  162. -- Update formspec, infotext and node
  163. --
  164. local formspec = inactive_formspec
  165. local item_state
  166. local item_percent = 0
  167. if cookable then
  168. item_percent = math.floor(src_time / cooked.time * 100)
  169. if item_percent > 100 then
  170. item_state = "100% (output full)"
  171. else
  172. item_state = item_percent .. "%"
  173. end
  174. else
  175. if srclist[1]:is_empty() then
  176. item_state = "Empty"
  177. else
  178. item_state = "Not cookable"
  179. end
  180. end
  181. local fuel_state = "Empty"
  182. local active = "inactive "
  183. local result = false
  184. if fuel_totaltime ~= 0 then
  185. active = "active "
  186. local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
  187. fuel_state = fuel_percent .. "%"
  188. formspec = active_formspec(fuel_percent, item_percent)
  189. swap_node(pos, "default:furnace_active")
  190. -- make sure timer restarts automatically
  191. result = true
  192. else
  193. if not fuellist[1]:is_empty() then
  194. fuel_state = "0%"
  195. end
  196. swap_node(pos, "default:furnace")
  197. -- stop timer on the inactive furnace
  198. minetest.get_node_timer(pos):stop()
  199. end
  200. local infotext = "Furnace " .. active .. "(Item: " .. item_state .. "; Fuel: " .. fuel_state .. ")"
  201. -- Set meta values
  202. meta:set_float("fuel_totaltime", fuel_totaltime)
  203. meta:set_float("fuel_time", fuel_time)
  204. meta:set_float("src_time", src_time)
  205. meta:set_string("formspec", formspec)
  206. meta:set_string("infotext", infotext)
  207. return result
  208. end
  209. --
  210. -- Node definitions
  211. --
  212. -- Stone furnace
  213. minetest.register_node("default:furnace", {
  214. description = "Furnace",
  215. tiles = {
  216. "default_furnace_top.png", "default_furnace_bottom.png",
  217. "default_furnace_side.png", "default_furnace_side.png",
  218. "default_furnace_side.png", "default_furnace_front.png"
  219. },
  220. paramtype2 = "facedir",
  221. groups = {cracky=3},
  222. legacy_facedir_simple = true,
  223. is_ground_content = false,
  224. sounds = default.node_sound_stone_defaults(),
  225. can_dig = can_dig,
  226. on_timer = furnace_node_timer,
  227. on_construct = function(pos)
  228. local meta = minetest.get_meta(pos)
  229. meta:set_string("formspec", inactive_formspec)
  230. local inv = meta:get_inventory()
  231. inv:set_size('src', 1)
  232. inv:set_size('fuel', 1)
  233. inv:set_size('dst', 1)
  234. end,
  235. on_metadata_inventory_move = function(pos)
  236. minetest.get_node_timer(pos):start(1.0)
  237. end,
  238. on_metadata_inventory_put = function(pos)
  239. -- start timer function, it will sort out whether furnace can burn or not.
  240. minetest.get_node_timer(pos):start(1.0)
  241. end,
  242. allow_metadata_inventory_put = allow_metadata_inventory_put,
  243. allow_metadata_inventory_move = allow_metadata_inventory_move,
  244. allow_metadata_inventory_take = allow_metadata_inventory_take,
  245. })
  246. minetest.register_node("default:furnace_active", {
  247. description = "Furnace",
  248. tiles = {
  249. "default_furnace_top.png", "default_furnace_bottom.png",
  250. "default_furnace_side.png", "default_furnace_side.png",
  251. "default_furnace_side.png",
  252. {
  253. image = "default_furnace_front_active.png",
  254. backface_culling = false,
  255. animation = {
  256. type = "vertical_frames",
  257. aspect_w = 16,
  258. aspect_h = 16,
  259. length = 1.5
  260. },
  261. }
  262. },
  263. paramtype2 = "facedir",
  264. light_source = 8,
  265. drop = "default:furnace",
  266. groups = {cracky=3, not_in_creative_inventory=1},
  267. is_ground_content = false,
  268. sounds = default.node_sound_stone_defaults(),
  269. on_timer = furnace_node_timer,
  270. can_dig = can_dig,
  271. allow_metadata_inventory_put = allow_metadata_inventory_put,
  272. allow_metadata_inventory_move = allow_metadata_inventory_move,
  273. allow_metadata_inventory_take = allow_metadata_inventory_take,
  274. })
  275. -- Clay furnace
  276. -- TODO: One function for all
  277. local function clay_furnace_node_timer(pos, elapsed)
  278. -- Inizialize metadata
  279. local meta = minetest.get_meta(pos)
  280. local fuel_time = meta:get_float("fuel_time") or 0
  281. local src_time = meta:get_float("src_time") or 0
  282. local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
  283. local inv = meta:get_inventory()
  284. local srclist, fuellist
  285. local cookable, cooked
  286. local fuel
  287. local update = true
  288. while elapsed > 0 and update do
  289. update = false
  290. srclist = inv:get_list("src")
  291. fuellist = inv:get_list("fuel")
  292. -- Cooking
  293. -- Check if we have cookable content
  294. local aftercooked
  295. cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
  296. cookable = cooked.time ~= 0
  297. local el = math.min(elapsed, fuel_totaltime - fuel_time)
  298. if cookable then -- fuel lasts long enough, adjust el to cooking duration
  299. el = math.min(el, cooked.time - src_time)
  300. end
  301. -- Check if we have enough fuel to burn
  302. if fuel_time < fuel_totaltime then
  303. -- The furnace is currently active and has enough fuel
  304. fuel_time = fuel_time + el
  305. -- If there is a cookable item then check if it is ready yet
  306. if cookable then
  307. src_time = src_time + el
  308. if src_time >= cooked.time then
  309. -- Place result in dst list if possible
  310. if inv:room_for_item("dst", cooked.item) then
  311. inv:add_item("dst", cooked.item)
  312. inv:set_stack("src", 1, aftercooked.items[1])
  313. src_time = src_time - cooked.time
  314. update = true
  315. end
  316. else
  317. -- Item could not be cooked: probably missing fuel
  318. update = true
  319. end
  320. end
  321. else
  322. -- Furnace ran out of fuel
  323. if cookable then
  324. -- We need to get new fuel
  325. local afterfuel
  326. fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
  327. if fuel.time == 0 then
  328. -- No valid fuel in fuel list
  329. fuel_totaltime = 0
  330. src_time = 0
  331. else
  332. -- Take fuel from fuel list
  333. inv:set_stack("fuel", 1, afterfuel.items[1])
  334. update = true
  335. fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time)
  336. end
  337. else
  338. -- We don't need to get new fuel since there is no cookable item
  339. fuel_totaltime = 0
  340. src_time = 0
  341. end
  342. fuel_time = 0
  343. end
  344. elapsed = elapsed - el
  345. end
  346. if fuel and fuel_totaltime > fuel.time then
  347. fuel_totaltime = fuel.time
  348. end
  349. if srclist[1]:is_empty() then
  350. src_time = 0
  351. end
  352. --
  353. -- Update formspec, infotext and node
  354. --
  355. local formspec = inactive_formspec
  356. local item_state
  357. local item_percent = 0
  358. if cookable then
  359. item_percent = math.floor(src_time / cooked.time * 100)
  360. if item_percent > 100 then
  361. item_state = "100% (output full)"
  362. else
  363. item_state = item_percent .. "%"
  364. end
  365. else
  366. if srclist[1]:is_empty() then
  367. item_state = "Empty"
  368. else
  369. item_state = "Not cookable"
  370. end
  371. end
  372. local fuel_state = "Empty"
  373. local active = "inactive "
  374. local result = false
  375. if fuel_totaltime ~= 0 then
  376. active = "active "
  377. local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
  378. fuel_state = fuel_percent .. "%"
  379. formspec = active_formspec(fuel_percent, item_percent)
  380. swap_node(pos, "default:clay_furnace_active")
  381. -- make sure timer restarts automatically
  382. result = true
  383. else
  384. if not fuellist[1]:is_empty() then
  385. fuel_state = "0%"
  386. end
  387. swap_node(pos, "default:clay_furnace")
  388. -- stop timer on the inactive furnace
  389. minetest.get_node_timer(pos):stop()
  390. end
  391. local infotext = "Furnace " .. active .. "(Item: " .. item_state .. "; Fuel: " .. fuel_state .. ")"
  392. -- Set meta values
  393. meta:set_float("fuel_totaltime", fuel_totaltime)
  394. meta:set_float("fuel_time", fuel_time)
  395. meta:set_float("src_time", src_time)
  396. meta:set_string("formspec", formspec)
  397. meta:set_string("infotext", infotext)
  398. return result
  399. end
  400. minetest.register_node("default:clay_furnace", {
  401. description = "Clay Furnace",
  402. tiles = {
  403. "default_clay_furnace_side.png", "default_clay_furnace_side.png",
  404. "default_clay_furnace_side.png", "default_clay_furnace_side.png",
  405. "default_clay_furnace_side.png", "default_clay_furnace_front.png"
  406. },
  407. paramtype2 = "facedir",
  408. drop = "",
  409. groups = {cracky=3},
  410. legacy_facedir_simple = true,
  411. is_ground_content = false,
  412. sounds = default.node_sound_stone_defaults(),
  413. can_dig = can_dig,
  414. on_timer = clay_furnace_node_timer,
  415. on_construct = function(pos)
  416. local meta = minetest.get_meta(pos)
  417. meta:set_string("formspec", inactive_formspec)
  418. local inv = meta:get_inventory()
  419. inv:set_size('src', 1)
  420. inv:set_size('fuel', 1)
  421. inv:set_size('dst', 1)
  422. end,
  423. on_metadata_inventory_move = function(pos)
  424. minetest.get_node_timer(pos):start(1.0)
  425. end,
  426. on_metadata_inventory_put = function(pos)
  427. -- start timer function, it will sort out whether furnace can burn or not.
  428. minetest.get_node_timer(pos):start(1.0)
  429. end,
  430. allow_metadata_inventory_put = allow_metadata_inventory_put,
  431. allow_metadata_inventory_move = allow_metadata_inventory_move,
  432. allow_metadata_inventory_take = allow_metadata_inventory_take,
  433. })
  434. minetest.register_node("default:clay_furnace_active", {
  435. description = "Clay Furnace",
  436. tiles = {
  437. "default_clay_furnace_side.png", "default_clay_furnace_side.png",
  438. "default_clay_furnace_side.png", "default_clay_furnace_side.png",
  439. "default_clay_furnace_side.png",
  440. {
  441. image = "default_clay_furnace_front_active.png",
  442. backface_culling = false,
  443. animation = {
  444. type = "vertical_frames",
  445. aspect_w = 16,
  446. aspect_h = 16,
  447. length = 1.5
  448. },
  449. }
  450. },
  451. paramtype2 = "facedir",
  452. light_source = 5,
  453. drop = "",
  454. groups = {cracky=3, not_in_creative_inventory=1},
  455. is_ground_content = false,
  456. sounds = default.node_sound_stone_defaults(),
  457. on_timer = clay_furnace_node_timer,
  458. can_dig = can_dig,
  459. allow_metadata_inventory_put = allow_metadata_inventory_put,
  460. allow_metadata_inventory_move = allow_metadata_inventory_move,
  461. allow_metadata_inventory_take = allow_metadata_inventory_take,
  462. })