furnace.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. -- default/furnace.lua
  2. -- support for MT game translation.
  3. local S = default.get_translator
  4. --
  5. -- Formspecs
  6. --
  7. function default.get_furnace_active_formspec(fuel_percent, item_percent)
  8. return 'size[8,8.5]'..
  9. 'list[context;src;2.75,0.5;1,1;]'..
  10. 'list[context;fuel;2.75,2.5;1,1;]'..
  11. 'image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:'..
  12. (fuel_percent)..':default_furnace_fire_fg.png]'..
  13. 'image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[lowpart:'..
  14. (item_percent)..':gui_furnace_arrow_fg.png^[transformR270]'..
  15. 'list[context;dst;4.75,0.96;2,2;]'..
  16. 'list[current_player;main;0,4.25;8,1;]'..
  17. 'list[current_player;main;0,5.5;8,3;8]'..
  18. 'listring[context;dst]'..
  19. 'listring[current_player;main]'..
  20. 'listring[context;src]'..
  21. 'listring[current_player;main]'..
  22. 'listring[context;fuel]'..
  23. 'listring[current_player;main]'..
  24. default.get_hotbar_bg(0, 4.25)
  25. end
  26. function default.get_furnace_inactive_formspec()
  27. return 'size[8,8.5]'..
  28. 'list[context;src;2.75,0.5;1,1;]'..
  29. 'list[context;fuel;2.75,2.5;1,1;]'..
  30. 'image[2.75,1.5;1,1;default_furnace_fire_bg.png]'..
  31. 'image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]'..
  32. 'list[context;dst;4.75,0.96;2,2;]'..
  33. 'list[current_player;main;0,4.25;8,1;]'..
  34. 'list[current_player;main;0,5.5;8,3;8]'..
  35. 'listring[context;dst]'..
  36. 'listring[current_player;main]'..
  37. 'listring[context;src]'..
  38. 'listring[current_player;main]'..
  39. 'listring[context;fuel]'..
  40. 'listring[current_player;main]'..
  41. default.get_hotbar_bg(0, 4.25)
  42. end
  43. --
  44. -- Node callback functions that are the same for active and inactive furnace
  45. --
  46. local function can_dig(pos, player)
  47. local meta = minetest.get_meta(pos);
  48. local inv = meta:get_inventory()
  49. return inv:is_empty('fuel') and inv:is_empty('dst') and inv:is_empty('src')
  50. end
  51. function default.is_hot_node(name)
  52. if name=='default:lava_source' or name=='hopper:hopper' then
  53. return true
  54. else
  55. return false
  56. end
  57. end
  58. local function param2_to_front(param2)
  59. if param2==0 then
  60. return {x=0,y=0,z=-1}
  61. elseif param2==1 then
  62. return {x=-1,y=0,z=0}
  63. elseif param2==2 then
  64. return {x=0,y=0,z=1}
  65. elseif param2==3 then
  66. return {x=1,y=0,z=0}
  67. end
  68. end
  69. local function allow_metadata_inventory_put(pos, listname, index, stack, player)
  70. if minetest.is_protected(pos, player:get_player_name()) then
  71. return 0
  72. end
  73. local meta = minetest.get_meta(pos)
  74. local inv = meta:get_inventory()
  75. if listname == 'fuel' then
  76. if minetest.get_craft_result({method='fuel', width=1, items={stack}}).time ~= 0 then
  77. if inv:is_empty('src') then
  78. meta:set_string('infotext', S('Furnace is empty'))
  79. end
  80. return stack:get_count()
  81. else
  82. return 0
  83. end
  84. elseif listname == 'src' then
  85. return stack:get_count()
  86. elseif listname == 'dst' then
  87. return 0
  88. end
  89. end
  90. function default.is_super_furnace(pos)--function to check if a furnace should smelt extra quickly
  91. local node = minetest.get_node(pos)
  92. if node.name~='default:furnace' and node.name~='default:furnace_active' then --not even a furnace
  93. return false
  94. end
  95. if node.param2 > 3 then --seriously? just have it face a reasonable direction people.
  96. return false
  97. end
  98. -- ok, so we check from pos + {x=1,y=1,z=1} to pos + {x=-1,y=-2,z=-1} for heat_shield
  99. -- except pos+{x=0,y=-1,z=0} has to be hot_node (lava, fire, etc.)
  100. -- and pos+param2_to_front(node.param2) has to be air
  101. local underpos = vector.add(pos,{x=0,y=-1,z=0})
  102. local frontpos = vector.add(pos,param2_to_front(node.param2))
  103. if not default.is_hot_node(minetest.get_node(underpos).name) then-- nothing to keep us extra hot.
  104. return false
  105. end
  106. if not minetest.get_node(frontpos).name=='air' then --we need somewhere to vent
  107. return false
  108. end
  109. local pos1 = {x=pos.x+1, y=pos.y, z=pos.z+1}
  110. local pos0 = {x=pos.x-1, y=pos.y-2, z=pos.z-1}
  111. local can_replace = minetest.find_nodes_in_area(pos0, pos1, 'default:lava_source')
  112. local replace_num = #can_replace
  113. if replace_num < 12 then
  114. return false
  115. end
  116. return true --yay! this furnace has succeded and can smelt extra quickly!
  117. end
  118. local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
  119. local meta = minetest.get_meta(pos)
  120. local inv = meta:get_inventory()
  121. local stack = inv:get_stack(from_list, from_index)
  122. return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
  123. end
  124. local function allow_metadata_inventory_take(pos, listname, index, stack, player)
  125. if minetest.is_protected(pos, player:get_player_name()) then
  126. return 0
  127. end
  128. return stack:get_count()
  129. end
  130. local function swap_node(pos, name)
  131. local node = minetest.get_node(pos)
  132. if node.name == name then
  133. return
  134. end
  135. node.name = name
  136. minetest.swap_node(pos, node)
  137. end
  138. local function custom_cooking_check(itemlist,pos)
  139. local meta = minetest.get_meta(pos)
  140. local super_furnace = meta:get_string('super')
  141. local cooked,aftercooked
  142. cooked,aftercooked = minetest.get_craft_result({method = 'cooking', width = 1, items = itemlist})
  143. if super_furnace == 'yes' then
  144. cooked.time = cooked.time/2 --or whatever speed increase u think is reasonable.
  145. end
  146. return cooked,aftercooked
  147. end
  148. local function furnace_node_timer(pos, elapsed)
  149. -- Initialize metadata
  150. --
  151. local meta = minetest.get_meta(pos)
  152. local fuel_time = meta:get_float('fuel_time') or 0
  153. local src_time = meta:get_float('src_time') or 0
  154. local fuel_totaltime = meta:get_float('fuel_totaltime') or 0
  155. local super_furnace = meta:get_string('super')
  156. if super_furnace == 'maybe' then
  157. if default.is_super_furnace(pos) then
  158. meta:set_string('super', 'yes')
  159. else
  160. meta:set_string('super', 'no')
  161. end
  162. end
  163. local inv = meta:get_inventory()
  164. local srclist, fuellist
  165. local dst_full = false
  166. local cookable, cooked
  167. local fuel
  168. local update = true
  169. while elapsed > 0 and update do
  170. update = false
  171. srclist = inv:get_list('src')
  172. fuellist = inv:get_list('fuel')
  173. --
  174. -- Cooking
  175. --
  176. -- Check if we have cookable content
  177. local aftercooked
  178. cooked, aftercooked = custom_cooking_check(srclist,pos)
  179. cookable = cooked.time ~= 0
  180. local el = math.min(elapsed, fuel_totaltime - fuel_time)
  181. if cookable then -- fuel lasts long enough, adjust el to cooking duration
  182. el = math.min(el, cooked.time - src_time)
  183. end
  184. -- Check if we have enough fuel to burn
  185. if fuel_time < fuel_totaltime then
  186. -- The furnace is currently active and has enough fuel
  187. fuel_time = fuel_time + el
  188. -- If there is a cookable item then check if it is ready yet
  189. if cookable then
  190. src_time = src_time + el
  191. if src_time >= cooked.time then
  192. -- Place result in dst list if possible
  193. if inv:room_for_item('dst', cooked.item) then
  194. inv:add_item('dst', cooked.item)
  195. inv:set_stack('src', 1, aftercooked.items[1])
  196. src_time = src_time - cooked.time
  197. update = true
  198. else
  199. dst_full = true
  200. end
  201. else
  202. -- Item could not be cooked: probably missing fuel
  203. update = true
  204. end
  205. end
  206. else
  207. -- Furnace ran out of fuel
  208. if cookable then
  209. -- We need to get new fuel
  210. local afterfuel
  211. fuel, afterfuel = minetest.get_craft_result({method = 'fuel', width = 1, items = fuellist})
  212. if fuel.time == 0 then
  213. -- No valid fuel in fuel list
  214. fuel_totaltime = 0
  215. src_time = 0
  216. else
  217. -- Take fuel from fuel list
  218. inv:set_stack('fuel', 1, afterfuel.items[1])
  219. -- Put replacements in dst list or drop them on the furnace.
  220. local replacements = fuel.replacements
  221. if replacements[1] then
  222. local leftover = inv:add_item('dst', replacements[1])
  223. if not leftover:is_empty() then
  224. local above = vector.new(pos.x, pos.y + 1, pos.z)
  225. local drop_pos = minetest.find_node_near(above, 1, {'air'}) or above
  226. minetest.item_drop(replacements[1], nil, drop_pos)
  227. end
  228. end
  229. update = true
  230. fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time)
  231. end
  232. else
  233. -- We don't need to get new fuel since there is no cookable item
  234. fuel_totaltime = 0
  235. src_time = 0
  236. end
  237. fuel_time = 0
  238. end
  239. elapsed = elapsed - el
  240. end
  241. if fuel and fuel_totaltime > fuel.time then
  242. fuel_totaltime = fuel.time
  243. end
  244. if srclist and srclist[1]:is_empty() then
  245. src_time = 0
  246. end
  247. --
  248. -- Update formspec, infotext and node
  249. --
  250. local formspec
  251. local item_state
  252. local item_percent = 0
  253. if cookable then
  254. item_percent = math.floor(src_time / cooked.time * 100)
  255. if dst_full then
  256. item_state = S('100% (output full)')
  257. else
  258. item_state = S('@1%', item_percent)
  259. end
  260. else
  261. if srclist and not srclist[1]:is_empty() then
  262. item_state = S('Not cookable')
  263. else
  264. item_state = S('Empty')
  265. end
  266. end
  267. local fuel_state = S('Empty')
  268. local active = false
  269. local result = false
  270. if fuel_totaltime ~= 0 then
  271. active = true
  272. local fuel_percent = 100 - math.floor(fuel_time / fuel_totaltime * 100)
  273. fuel_state = S('@1%', fuel_percent)
  274. formspec = default.get_furnace_active_formspec(fuel_percent, item_percent)
  275. swap_node(pos, 'default:furnace_active')
  276. -- make sure timer restarts automatically
  277. result = true
  278. else
  279. if fuellist and not fuellist[1]:is_empty() then
  280. fuel_state = S('@1%', 0)
  281. end
  282. formspec = default.get_furnace_inactive_formspec()
  283. swap_node(pos, 'default:furnace')
  284. -- stop timer on the inactive furnace
  285. minetest.get_node_timer(pos):stop()
  286. meta:set_string('super', 'maybe')
  287. end
  288. local infotext
  289. if active then
  290. infotext = S('Furnace active')
  291. else
  292. infotext = S('Furnace inactive')
  293. end
  294. infotext = infotext .. '\n' .. S('(Item: @1; Fuel: @2)', item_state, fuel_state)
  295. --
  296. -- Set meta values
  297. --
  298. meta:set_float('fuel_totaltime', fuel_totaltime)
  299. meta:set_float('fuel_time', fuel_time)
  300. meta:set_float('src_time', src_time)
  301. meta:set_string('formspec', formspec)
  302. meta:set_string('infotext', infotext)
  303. return result
  304. end
  305. --added by idiot12 from here
  306. --
  307. -- Node definitions
  308. --
  309. minetest.register_node('default:furnace', {
  310. description = S('Furnace'),
  311. tiles = {
  312. 'default_furnace_top.png', 'default_furnace_bottom.png',
  313. 'default_furnace_side.png', 'default_furnace_side.png',
  314. 'default_furnace_side.png', 'default_furnace_front.png'
  315. },
  316. paramtype2 = 'facedir',
  317. groups = {cracky=2},
  318. legacy_facedir_simple = true,
  319. is_ground_content = false,
  320. sounds = default.node_sound_stone_defaults(),
  321. can_dig = can_dig,
  322. on_timer = furnace_node_timer,
  323. on_construct = function(pos)
  324. local meta = minetest.get_meta(pos)
  325. meta:set_string('super', 'maybe')
  326. local inv = meta:get_inventory()
  327. inv:set_size('src', 1)
  328. inv:set_size('fuel', 1)
  329. inv:set_size('dst', 4)
  330. furnace_node_timer(pos, 0)
  331. end,
  332. on_metadata_inventory_move = function(pos)
  333. minetest.get_node_timer(pos):start(1.0)
  334. end,
  335. on_metadata_inventory_put = function(pos)
  336. -- start timer function, it will sort out whether furnace can burn or not.
  337. minetest.get_node_timer(pos):start(1.0)
  338. end,
  339. on_blast = function(pos)
  340. local drops = {}
  341. default.get_inventory_drops(pos, 'src', drops)
  342. default.get_inventory_drops(pos, 'fuel', drops)
  343. default.get_inventory_drops(pos, 'dst', drops)
  344. drops[#drops+1] = 'default:furnace'
  345. minetest.remove_node(pos)
  346. return drops
  347. end,
  348. allow_metadata_inventory_put = allow_metadata_inventory_put,
  349. allow_metadata_inventory_move = allow_metadata_inventory_move,
  350. allow_metadata_inventory_take = allow_metadata_inventory_take,
  351. })
  352. minetest.register_node('default:furnace_active', {
  353. description = S('Furnace'),
  354. tiles = {
  355. 'default_furnace_top.png', 'default_furnace_bottom.png',
  356. 'default_furnace_side.png', 'default_furnace_side.png',
  357. 'default_furnace_side.png',
  358. {
  359. image = 'default_furnace_front_active.png',
  360. backface_culling = false,
  361. animation = {
  362. type = 'vertical_frames',
  363. aspect_w = 16,
  364. aspect_h = 16,
  365. length = 1.5
  366. },
  367. }
  368. },
  369. paramtype2 = 'facedir',
  370. light_source = 8,
  371. drop = 'default:furnace',
  372. groups = {cracky=2, not_in_creative_inventory=1},
  373. legacy_facedir_simple = true,
  374. is_ground_content = false,
  375. sounds = default.node_sound_stone_defaults(),
  376. on_timer = furnace_node_timer,
  377. can_dig = can_dig,
  378. allow_metadata_inventory_put = allow_metadata_inventory_put,
  379. allow_metadata_inventory_move = allow_metadata_inventory_move,
  380. allow_metadata_inventory_take = allow_metadata_inventory_take,
  381. })
  382. minetest.register_craft({
  383. output = 'default:furnace',
  384. recipe = {
  385. {'group:stone', 'group:stone', 'group:stone'},
  386. {'group:stone', '', 'group:stone'},
  387. {'group:stone', 'group:stone', 'group:stone'},
  388. }
  389. })