Update helptext of obsidian
[MineClone/MineClone2.git] / mods / ITEMS / mcl_furnaces / init.lua
blob39103f18f1d41d2a73f685e78486696872448e79
2 local S = minetest.get_translator("mcl_furnaces")
4 local LIGHT_ACTIVE_FURNACE = 13
6 --
7 -- Formspecs
8 --
10 local function active_formspec(fuel_percent, item_percent)
11 return "size[9,8.75]"..
12 "label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
13 "list[current_player;main;0,4.5;9,3;9]"..
14 mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
15 "list[current_player;main;0,7.74;9,1;]"..
16 mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
17 "label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
18 "list[current_name;src;2.75,0.5;1,1;]"..
19 mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
20 "list[current_name;fuel;2.75,2.5;1,1;]"..
21 mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
22 "list[current_name;dst;5.75,1.5;1,1;]"..
23 mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
24 "image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
25 (100-fuel_percent)..":default_furnace_fire_fg.png]"..
26 "image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:"..
27 (item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
28 -- Craft guide button temporarily removed due to Minetest bug.
29 -- TODO: Add it back when the Minetest bug is fixed.
30 --"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
31 --"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
32 "listring[current_name;dst]"..
33 "listring[current_player;main]"..
34 "listring[current_name;src]"..
35 "listring[current_player;main]"..
36 "listring[current_name;fuel]"..
37 "listring[current_player;main]"
38 end
40 local inactive_formspec = "size[9,8.75]"..
41 "label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
42 "list[current_player;main;0,4.5;9,3;9]"..
43 mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
44 "list[current_player;main;0,7.74;9,1;]"..
45 mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
46 "label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
47 "list[current_name;src;2.75,0.5;1,1;]"..
48 mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
49 "list[current_name;fuel;2.75,2.5;1,1;]"..
50 mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
51 "list[current_name;dst;5.75,1.5;1,1;]"..
52 mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
53 "image[2.75,1.5;1,1;default_furnace_fire_bg.png]"..
54 "image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[transformR270]"..
55 -- Craft guide button temporarily removed due to Minetest bug.
56 -- TODO: Add it back when the Minetest bug is fixed.
57 --"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
58 --"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
59 "listring[current_name;dst]"..
60 "listring[current_player;main]"..
61 "listring[current_name;src]"..
62 "listring[current_player;main]"..
63 "listring[current_name;fuel]"..
64 "listring[current_player;main]"
66 local receive_fields = function(pos, formname, fields, sender)
67 if fields.craftguide then
68 mcl_craftguide.show(sender:get_player_name())
69 end
70 end
73 -- Node callback functions that are the same for active and inactive furnace
76 local function allow_metadata_inventory_put(pos, listname, index, stack, player)
77 local name = player:get_player_name()
78 if minetest.is_protected(pos, name) then
79 minetest.record_protection_violation(pos, name)
80 return 0
81 end
82 local meta = minetest.get_meta(pos)
83 local inv = meta:get_inventory()
84 if listname == "fuel" then
85 -- Special case: empty bucket (not a fuel, but used for sponge drying)
86 if stack:get_name() == "mcl_buckets:bucket_empty" then
87 if inv:get_stack(listname, index):get_count() == 0 then
88 return 1
89 else
90 return 0
91 end
92 end
94 -- Test stack with size 1 because we burn one fuel at a time
95 local teststack = ItemStack(stack)
96 teststack:set_count(1)
97 local output, decremented_input = minetest.get_craft_result({method="fuel", width=1, items={teststack}})
98 if output.time ~= 0 then
99 -- Only allow to place 1 item if fuel get replaced by recipe.
100 -- This is the case for lava buckets.
101 local replace_item = decremented_input.items[1]
102 if replace_item:is_empty() then
103 -- For most fuels, just allow to place everything
104 return stack:get_count()
105 else
106 if inv:get_stack(listname, index):get_count() == 0 then
107 return 1
108 else
109 return 0
112 else
113 return 0
115 elseif listname == "src" then
116 return stack:get_count()
117 elseif listname == "dst" then
118 return 0
122 local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
123 local meta = minetest.get_meta(pos)
124 local inv = meta:get_inventory()
125 local stack = inv:get_stack(from_list, from_index)
126 return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
129 local function allow_metadata_inventory_take(pos, listname, index, stack, player)
130 local name = player:get_player_name()
131 if minetest.is_protected(pos, name) then
132 minetest.record_protection_violation(pos, name)
133 return 0
135 return stack:get_count()
138 local function on_metadata_inventory_take(pos, listname, index, stack, player)
139 -- Award smelting achievements
140 if listname == "dst" then
141 if stack:get_name() == "mcl_core:iron_ingot" then
142 awards.unlock(player:get_player_name(), "mcl:acquireIron")
143 elseif stack:get_name() == "mcl_fishing:fish_cooked" then
144 awards.unlock(player:get_player_name(), "mcl:cookFish")
149 local function spawn_flames(pos, param2)
150 local minrelpos, maxrelpos
151 local dir = minetest.facedir_to_dir(param2)
152 if dir.x > 0 then
153 minrelpos = { x = -0.6, y = -0.05, z = -0.25 }
154 maxrelpos = { x = -0.55, y = -0.45, z = 0.25 }
155 elseif dir.x < 0 then
156 minrelpos = { x = 0.55, y = -0.05, z = -0.25 }
157 maxrelpos = { x = 0.6, y = -0.45, z = 0.25 }
158 elseif dir.z > 0 then
159 minrelpos = { x = -0.25, y = -0.05, z = -0.6 }
160 maxrelpos = { x = 0.25, y = -0.45, z = -0.55 }
161 elseif dir.z < 0 then
162 minrelpos = { x = -0.25, y = -0.05, z = 0.55 }
163 maxrelpos = { x = 0.25, y = -0.45, z = 0.6 }
164 else
165 return
167 mcl_particles.add_node_particlespawner(pos, {
168 amount = 4,
169 time = 0,
170 minpos = vector.add(pos, minrelpos),
171 maxpos = vector.add(pos, maxrelpos),
172 minvel = { x = -0.01, y = 0, z = -0.01 },
173 maxvel = { x = 0.01, y = 0.1, z = 0.01 },
174 minexptime = 0.3,
175 maxexptime = 0.6,
176 minsize = 0.4,
177 maxsize = 0.8,
178 texture = "mcl_particles_flame.png",
179 glow = LIGHT_ACTIVE_FURNACE,
180 }, "low")
183 local function swap_node(pos, name)
184 local node = minetest.get_node(pos)
185 if node.name == name then
186 return
188 node.name = name
189 minetest.swap_node(pos, node)
190 if name == "mcl_furnaces:furnace_active" then
191 spawn_flames(pos, node.param2)
192 else
193 mcl_particles.delete_node_particlespawners(pos)
197 local function furnace_reset_delta_time(pos)
198 local meta = minetest.get_meta(pos)
199 local time_speed = tonumber(minetest.settings:get('time_speed') or 72)
200 if (time_speed < 0.1) then
201 return
203 local time_multiplier = 86400 / time_speed
204 local current_game_time = .0 + ((minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier)
206 -- TODO: Change meta:get/set_string() to get/set_float() for 'last_gametime'.
207 -- In Windows *_float() works OK but under Linux it returns rounded unusable values like 449540.000000000
208 local last_game_time = meta:get_string("last_gametime")
209 if last_game_time then
210 last_game_time = tonumber(last_game_time)
212 if not last_game_time or last_game_time < 1 or math.abs(last_game_time - current_game_time) <= 1.5 then
213 return
216 meta:set_string("last_gametime", tostring(current_game_time))
219 local function furnace_get_delta_time(pos, elapsed)
220 local meta = minetest.get_meta(pos)
221 local time_speed = tonumber(minetest.settings:get('time_speed') or 72)
222 local current_game_time
223 if (time_speed < 0.1) then
224 return meta, elapsed
225 else
226 local time_multiplier = 86400 / time_speed
227 current_game_time = .0 + ((minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier)
230 local last_game_time = meta:get_string("last_gametime")
231 if last_game_time then
232 last_game_time = tonumber(last_game_time)
234 if not last_game_time or last_game_time < 1 then
235 last_game_time = current_game_time - 0.1
236 elseif last_game_time == current_game_time then
237 current_game_time = current_game_time + 1.0
240 local elapsed_game_time = .0 + current_game_time - last_game_time
242 meta:set_string("last_gametime", tostring(current_game_time))
244 return meta, elapsed_game_time
247 local function furnace_node_timer(pos, elapsed)
249 -- Inizialize metadata
251 local meta, elapsed_game_time = furnace_get_delta_time(pos, elapsed)
253 local fuel_time = meta:get_float("fuel_time") or 0
254 local src_time = meta:get_float("src_time") or 0
255 local src_item = meta:get_string("src_item") or ""
256 local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
258 local inv = meta:get_inventory()
259 local srclist, fuellist
261 local cookable, cooked
262 local active = true
263 local fuel
265 srclist = inv:get_list("src")
266 fuellist = inv:get_list("fuel")
268 -- Check if src item has been changed
269 if srclist[1]:get_name() ~= src_item then
270 -- Reset cooking progress in this case
271 src_time = 0
272 src_item = srclist[1]:get_name()
275 local update = true
276 while elapsed_game_time > 0.00001 and update do
278 -- Cooking
281 local el = elapsed_game_time
283 -- Check if we have cookable content: cookable
284 local aftercooked
285 cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
286 cookable = cooked.time ~= 0
287 if cookable then
288 -- Successful cooking requires space in dst slot and time
289 if not inv:room_for_item("dst", cooked.item) then
290 cookable = false
294 if cookable then -- fuel lasts long enough, adjust el to cooking duration
295 el = math.min(el, cooked.time - src_time)
298 -- Check if we have enough fuel to burn
299 active = fuel_time < fuel_totaltime
300 if cookable and not active then
301 -- We need to get new fuel
302 local afterfuel
303 fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
305 if fuel.time == 0 then
306 -- No valid fuel in fuel list -- stop
307 fuel_totaltime = 0
308 src_time = 0
309 update = false
310 else
311 -- Take fuel from fuel list
312 inv:set_stack("fuel", 1, afterfuel.items[1])
313 fuel_time = 0
314 fuel_totaltime = fuel.time
315 el = math.min(el, fuel_totaltime)
316 active = true
317 fuellist = inv:get_list("fuel")
319 elseif active then
320 el = math.min(el, fuel_totaltime - fuel_time)
321 -- The furnace is currently active and has enough fuel
322 fuel_time = fuel_time + el
325 -- If there is a cookable item then check if it is ready yet
326 if cookable and active then
327 src_time = src_time + el
328 -- Place result in dst list if done
329 if src_time >= cooked.time then
330 inv:add_item("dst", cooked.item)
331 inv:set_stack("src", 1, aftercooked.items[1])
333 -- Unique recipe: Pour water into empty bucket after cooking wet sponge successfully
334 if inv:get_stack("fuel", 1):get_name() == "mcl_buckets:bucket_empty" then
335 if srclist[1]:get_name() == "mcl_sponges:sponge_wet" then
336 inv:set_stack("fuel", 1, "mcl_buckets:bucket_water")
337 fuellist = inv:get_list("fuel")
338 -- Also for river water
339 elseif srclist[1]:get_name() == "mcl_sponges:sponge_wet_river_water" then
340 inv:set_stack("fuel", 1, "mcl_buckets:bucket_river_water")
341 fuellist = inv:get_list("fuel")
345 srclist = inv:get_list("src")
346 src_time = 0
350 elapsed_game_time = elapsed_game_time - el
353 if fuel and fuel_totaltime > fuel.time then
354 fuel_totaltime = fuel.time
356 if srclist and srclist[1]:is_empty() then
357 src_time = 0
361 -- Update formspec and node
363 local formspec = inactive_formspec
364 local item_state
365 local item_percent = 0
366 if cookable then
367 item_percent = math.floor(src_time / cooked.time * 100)
370 local result = false
372 if active then
373 local fuel_percent = 0
374 if fuel_totaltime > 0 then
375 fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
377 formspec = active_formspec(fuel_percent, item_percent)
378 swap_node(pos, "mcl_furnaces:furnace_active")
379 -- make sure timer restarts automatically
380 result = true
381 else
382 swap_node(pos, "mcl_furnaces:furnace")
383 -- stop timer on the inactive furnace
384 minetest.get_node_timer(pos):stop()
388 -- Set meta values
390 meta:set_float("fuel_totaltime", fuel_totaltime)
391 meta:set_float("fuel_time", fuel_time)
392 meta:set_float("src_time", src_time)
393 if srclist then
394 meta:set_string("src_item", srclist[1]:get_name())
395 else
396 meta:set_string("src_item", "")
398 meta:set_string("formspec", formspec)
400 return result
403 local on_rotate, after_rotate_active
404 if minetest.get_modpath("screwdriver") then
405 on_rotate = screwdriver.rotate_simple
406 after_rotate_active = function(pos)
407 local node = minetest.get_node(pos)
408 mcl_particles.delete_node_particlespawners(pos)
409 if node.name == "mcl_furnaces:furnace" then
410 return
412 spawn_flames(pos, node.param2)
416 minetest.register_node("mcl_furnaces:furnace", {
417 description = S("Furnace"),
418 _tt_help = S("Uses fuel to smelt or cook items"),
419 _doc_items_longdesc = S("Furnaces cook or smelt several items, using a furnace fuel, into something else."),
420 _doc_items_usagehelp =
421 S("Use the furnace to open the furnace menu. Place a furnace fuel in the lower slot and the source material in the upper slot. The furnace will slowly use its fuel to smelt the item. The result will be placed into the output slot at the right side.").."\n"..
422 S("Use the recipe book to see what you can smelt, what you can use as fuel and how long it will burn."),
423 _doc_items_hidden = false,
424 tiles = {
425 "default_furnace_top.png", "default_furnace_bottom.png",
426 "default_furnace_side.png", "default_furnace_side.png",
427 "default_furnace_side.png", "default_furnace_front.png"
429 paramtype2 = "facedir",
430 groups = {pickaxey=1, container=4, deco_block=1, material_stone=1},
431 is_ground_content = false,
432 sounds = mcl_sounds.node_sound_stone_defaults(),
434 on_timer = furnace_node_timer,
435 after_dig_node = function(pos, oldnode, oldmetadata, digger)
436 local meta = minetest.get_meta(pos)
437 local meta2 = meta
438 meta:from_table(oldmetadata)
439 local inv = meta:get_inventory()
440 for _, listname in ipairs({"src", "dst", "fuel"}) do
441 local stack = inv:get_stack(listname, 1)
442 if not stack:is_empty() then
443 local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
444 minetest.add_item(p, stack)
447 meta:from_table(meta2:to_table())
448 end,
450 on_construct = function(pos)
451 local meta = minetest.get_meta(pos)
452 meta:set_string("formspec", inactive_formspec)
453 local inv = meta:get_inventory()
454 inv:set_size('src', 1)
455 inv:set_size('fuel', 1)
456 inv:set_size('dst', 1)
457 end,
458 on_destruct = function(pos)
459 mcl_particles.delete_node_particlespawners(pos)
460 end,
462 on_metadata_inventory_move = function(pos)
463 -- Reset accumulated game time when player works with furnace:
464 furnace_reset_delta_time(pos)
465 minetest.get_node_timer(pos):start(1.0)
466 end,
467 on_metadata_inventory_put = function(pos)
468 -- Reset accumulated game time when player works with furnace:
469 furnace_reset_delta_time(pos)
470 -- start timer function, it will sort out whether furnace can burn or not.
471 minetest.get_node_timer(pos):start(1.0)
472 end,
473 on_metadata_inventory_take = function(pos)
474 -- Reset accumulated game time when player works with furnace:
475 furnace_reset_delta_time(pos)
476 -- start timer function, it will helpful if player clears dst slot
477 minetest.get_node_timer(pos):start(1.0)
478 end,
480 allow_metadata_inventory_put = allow_metadata_inventory_put,
481 allow_metadata_inventory_move = allow_metadata_inventory_move,
482 allow_metadata_inventory_take = allow_metadata_inventory_take,
483 on_receive_fields = receive_fields,
484 _mcl_blast_resistance = 3.5,
485 _mcl_hardness = 3.5,
486 on_rotate = on_rotate,
489 minetest.register_node("mcl_furnaces:furnace_active", {
490 description = S("Burning Furnace"),
491 _doc_items_create_entry = false,
492 tiles = {
493 "default_furnace_top.png", "default_furnace_bottom.png",
494 "default_furnace_side.png", "default_furnace_side.png",
495 "default_furnace_side.png", "default_furnace_front_active.png",
497 paramtype2 = "facedir",
498 paramtype = "light",
499 light_source = LIGHT_ACTIVE_FURNACE,
500 drop = "mcl_furnaces:furnace",
501 groups = {pickaxey=1, container=4, deco_block=1, not_in_creative_inventory=1, material_stone=1},
502 is_ground_content = false,
503 sounds = mcl_sounds.node_sound_stone_defaults(),
504 on_timer = furnace_node_timer,
506 after_dig_node = function(pos, oldnode, oldmetadata, digger)
507 local meta = minetest.get_meta(pos)
508 local meta2 = meta
509 meta:from_table(oldmetadata)
510 local inv = meta:get_inventory()
511 for _, listname in ipairs({"src", "dst", "fuel"}) do
512 local stack = inv:get_stack(listname, 1)
513 if not stack:is_empty() then
514 local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
515 minetest.add_item(p, stack)
518 meta:from_table(meta2:to_table())
519 end,
521 on_construct = function(pos)
522 local node = minetest.get_node(pos)
523 spawn_flames(pos, node.param2)
524 end,
525 on_destruct = function(pos)
526 mcl_particles.delete_node_particlespawners(pos)
527 end,
529 allow_metadata_inventory_put = allow_metadata_inventory_put,
530 allow_metadata_inventory_move = allow_metadata_inventory_move,
531 allow_metadata_inventory_take = allow_metadata_inventory_take,
532 on_metadata_inventory_take = on_metadata_inventory_take,
533 on_receive_fields = receive_fields,
534 _mcl_blast_resistance = 3.5,
535 _mcl_hardness = 3.5,
536 on_rotate = on_rotate,
537 after_rotate = after_rotate_active,
540 minetest.register_craft({
541 output = "mcl_furnaces:furnace",
542 recipe = {
543 { "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble" },
544 { "mcl_core:cobble", "", "mcl_core:cobble" },
545 { "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble" },
549 -- Add entry alias for the Help
550 if minetest.get_modpath("doc") then
551 doc.add_entry_alias("nodes", "mcl_furnaces:furnace", "nodes", "mcl_furnaces:furnace_active")
554 minetest.register_lbm({
555 label = "Active furnace flame particles",
556 name = "mcl_furnaces:flames",
557 nodenames = {"mcl_furnaces:furnace_active"},
558 run_at_every_load = true,
559 action = function(pos, node)
560 spawn_flames(pos, node.param2)
561 end,
564 -- Legacy
565 minetest.register_lbm({
566 label = "Update furnace formspecs (0.60.0)",
567 name = "mcl_furnaces:update_formspecs_0_60_0",
568 -- Only update inactive furnaces because active ones should update themselves
569 nodenames = { "mcl_furnaces:furnace" },
570 run_at_every_load = false,
571 action = function(pos, node)
572 local meta = minetest.get_meta(pos)
573 meta:set_string("formspec", inactive_formspec)
574 end,