Disable some demanding particles by default
[MineClone/MineClone2.git] / mods / ITEMS / mcl_furnaces / init.lua
blobce58b8c9a8f103bb1b415dfb7a63643454fca310
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 swap_node(pos, name)
150 local node = minetest.get_node(pos)
151 if node.name == name then
152 return
154 node.name = name
155 minetest.swap_node(pos, node)
158 local function furnace_reset_delta_time(pos)
159 local meta = minetest.get_meta(pos)
160 local time_multiplier = 86400 / (minetest.settings:get('time_speed') or 72)
161 local current_game_time = .0 + ((minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier)
163 -- TODO: Change meta:get/set_string() to get/set_float() for 'last_gametime'.
164 -- In Windows *_float() works OK but under Linux it returns rounded unusable values like 449540.000000000
165 local last_game_time = meta:get_string("last_gametime")
166 if last_game_time then
167 last_game_time = tonumber(last_game_time)
169 if not last_game_time or last_game_time < 1 or math.abs(last_game_time - current_game_time) <= 1.5 then
170 return
173 meta:set_string("last_gametime", tostring(current_game_time))
176 local function furnace_get_delta_time(pos)
177 local meta = minetest.get_meta(pos)
178 local time_multiplier = 86400 / (minetest.settings:get('time_speed') or 72)
179 local current_game_time = .0 + ((minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier)
181 local last_game_time = meta:get_string("last_gametime")
182 if last_game_time then
183 last_game_time = tonumber(last_game_time)
185 if not last_game_time or last_game_time < 1 then
186 last_game_time = current_game_time
187 elseif last_game_time == current_game_time then
188 current_game_time = current_game_time + 1.0
191 local elapsed_game_time = .0 + current_game_time - last_game_time
193 meta:set_string("last_gametime", tostring(current_game_time))
195 return meta, elapsed_game_time
198 local function furnace_node_timer(pos, elapsed)
200 -- Inizialize metadata
202 local meta, elapsed_game_time = furnace_get_delta_time(pos)
204 local fuel_time = meta:get_float("fuel_time") or 0
205 local src_time = meta:get_float("src_time") or 0
206 local src_item = meta:get_string("src_item") or ""
207 local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
209 local inv = meta:get_inventory()
210 local srclist, fuellist
212 local cookable, cooked
213 local active
214 local fuel
216 srclist = inv:get_list("src")
217 fuellist = inv:get_list("fuel")
219 -- Check if src item has been changed
220 if srclist[1]:get_name() ~= src_item then
221 -- Reset cooking progress in this case
222 src_time = 0
223 src_item = srclist[1]:get_name()
226 local update = true
227 while elapsed_game_time > 0.00001 and update do
229 -- Cooking
232 local el = elapsed_game_time
234 -- Check if we have cookable content: cookable
235 local aftercooked
236 cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
237 cookable = cooked.time ~= 0
238 if cookable then
239 -- Successful cooking requires space in dst slot and time
240 if not inv:room_for_item("dst", cooked.item) then
241 cookable = false
245 if cookable then -- fuel lasts long enough, adjust el to cooking duration
246 el = math.min(el, cooked.time - src_time)
249 -- Check if we have enough fuel to burn
250 active = fuel_time < fuel_totaltime
251 if cookable and not active then
252 -- We need to get new fuel
253 local afterfuel
254 fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
256 if fuel.time == 0 then
257 -- No valid fuel in fuel list -- stop
258 fuel_totaltime = 0
259 src_time = 0
260 update = false
261 else
262 -- Take fuel from fuel list
263 inv:set_stack("fuel", 1, afterfuel.items[1])
264 fuel_time = 0
265 fuel_totaltime = fuel.time
266 el = math.min(el, fuel_totaltime)
267 active = true
268 fuellist = inv:get_list("fuel")
270 elseif active then
271 el = math.min(el, fuel_totaltime - fuel_time)
272 -- The furnace is currently active and has enough fuel
273 fuel_time = fuel_time + el
276 -- If there is a cookable item then check if it is ready yet
277 if cookable and active then
278 src_time = src_time + el
279 -- Place result in dst list if done
280 if src_time >= cooked.time then
281 inv:add_item("dst", cooked.item)
282 inv:set_stack("src", 1, aftercooked.items[1])
284 -- Unique recipe: Pour water into empty bucket after cooking wet sponge successfully
285 if inv:get_stack("fuel", 1):get_name() == "mcl_buckets:bucket_empty" then
286 if srclist[1]:get_name() == "mcl_sponges:sponge_wet" then
287 inv:set_stack("fuel", 1, "mcl_buckets:bucket_water")
288 fuellist = inv:get_list("fuel")
289 -- Also for river water
290 elseif srclist[1]:get_name() == "mcl_sponges:sponge_wet_river_water" then
291 inv:set_stack("fuel", 1, "mcl_buckets:bucket_river_water")
292 fuellist = inv:get_list("fuel")
296 srclist = inv:get_list("src")
297 src_time = 0
301 elapsed_game_time = elapsed_game_time - el
304 if fuel and fuel_totaltime > fuel.time then
305 fuel_totaltime = fuel.time
307 if srclist and srclist[1]:is_empty() then
308 src_time = 0
312 -- Update formspec and node
314 local formspec = inactive_formspec
315 local item_state
316 local item_percent = 0
317 if cookable then
318 item_percent = math.floor(src_time / cooked.time * 100)
321 local result = false
323 if active then
324 local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
325 formspec = active_formspec(fuel_percent, item_percent)
326 swap_node(pos, "mcl_furnaces:furnace_active")
327 -- make sure timer restarts automatically
328 result = true
329 else
330 swap_node(pos, "mcl_furnaces:furnace")
331 -- stop timer on the inactive furnace
332 minetest.get_node_timer(pos):stop()
336 -- Set meta values
338 meta:set_float("fuel_totaltime", fuel_totaltime)
339 meta:set_float("fuel_time", fuel_time)
340 meta:set_float("src_time", src_time)
341 if srclist then
342 meta:set_string("src_item", srclist[1]:get_name())
343 else
344 meta:set_string("src_item", "")
346 meta:set_string("formspec", formspec)
348 return result
351 local function spawn_flames(pos, param2)
352 local minrelpos, maxrelpos
353 local dir = minetest.facedir_to_dir(param2)
354 if dir.x > 0 then
355 minrelpos = { x = -0.6, y = -0.05, z = -0.25 }
356 maxrelpos = { x = -0.55, y = -0.45, z = 0.25 }
357 elseif dir.x < 0 then
358 minrelpos = { x = 0.55, y = -0.05, z = -0.25 }
359 maxrelpos = { x = 0.6, y = -0.45, z = 0.25 }
360 elseif dir.z > 0 then
361 minrelpos = { x = -0.25, y = -0.05, z = -0.6 }
362 maxrelpos = { x = 0.25, y = -0.45, z = -0.55 }
363 elseif dir.z < 0 then
364 minrelpos = { x = -0.25, y = -0.05, z = 0.55 }
365 maxrelpos = { x = 0.25, y = -0.45, z = 0.6 }
366 else
367 return
369 mcl_particles.add_node_particlespawner(pos, {
370 amount = 4,
371 time = 0,
372 minpos = vector.add(pos, minrelpos),
373 maxpos = vector.add(pos, maxrelpos),
374 minvel = { x = -0.01, y = 0, z = -0.01 },
375 maxvel = { x = 0.01, y = 0.1, z = 0.01 },
376 minexptime = 0.3,
377 maxexptime = 0.6,
378 minsize = 0.4,
379 maxsize = 0.8,
380 texture = "mcl_particles_flame.png",
381 glow = LIGHT_ACTIVE_FURNACE,
382 }, "low")
385 local on_rotate, after_rotate_active
386 if minetest.get_modpath("screwdriver") then
387 on_rotate = screwdriver.rotate_simple
388 after_rotate_active = function(pos)
389 local node = minetest.get_node(pos)
390 mcl_particles.delete_node_particlespawners(pos)
391 spawn_flames(pos, node.param2)
395 minetest.register_node("mcl_furnaces:furnace", {
396 description = S("Furnace"),
397 _tt_help = S("Uses fuel to smelt or cook items"),
398 _doc_items_longdesc = S("Furnaces cook or smelt several items, using a furnace fuel, into something else."),
399 _doc_items_usagehelp =
400 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"..
401 S("Use the recipe book to see what you can smelt, what you can use as fuel and how long it will burn."),
402 _doc_items_hidden = false,
403 tiles = {
404 "default_furnace_top.png", "default_furnace_bottom.png",
405 "default_furnace_side.png", "default_furnace_side.png",
406 "default_furnace_side.png", "default_furnace_front.png"
408 paramtype2 = "facedir",
409 groups = {pickaxey=1, container=4, deco_block=1, material_stone=1},
410 is_ground_content = false,
411 sounds = mcl_sounds.node_sound_stone_defaults(),
413 on_timer = furnace_node_timer,
414 after_dig_node = function(pos, oldnode, oldmetadata, digger)
415 local meta = minetest.get_meta(pos)
416 local meta2 = meta
417 meta:from_table(oldmetadata)
418 local inv = meta:get_inventory()
419 for _, listname in ipairs({"src", "dst", "fuel"}) do
420 local stack = inv:get_stack(listname, 1)
421 if not stack:is_empty() then
422 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}
423 minetest.add_item(p, stack)
426 meta:from_table(meta2:to_table())
427 end,
429 on_construct = function(pos)
430 local meta = minetest.get_meta(pos)
431 meta:set_string("formspec", inactive_formspec)
432 local inv = meta:get_inventory()
433 inv:set_size('src', 1)
434 inv:set_size('fuel', 1)
435 inv:set_size('dst', 1)
436 end,
438 on_metadata_inventory_move = function(pos)
439 -- Reset accumulated game time when player works with furnace:
440 furnace_reset_delta_time(pos)
441 minetest.get_node_timer(pos):start(1.0)
442 end,
443 on_metadata_inventory_put = function(pos)
444 -- Reset accumulated game time when player works with furnace:
445 furnace_reset_delta_time(pos)
446 -- start timer function, it will sort out whether furnace can burn or not.
447 minetest.get_node_timer(pos):start(1.0)
448 end,
449 on_metadata_inventory_take = function(pos)
450 -- Reset accumulated game time when player works with furnace:
451 furnace_reset_delta_time(pos)
452 -- start timer function, it will helpful if player clears dst slot
453 minetest.get_node_timer(pos):start(1.0)
454 end,
456 allow_metadata_inventory_put = allow_metadata_inventory_put,
457 allow_metadata_inventory_move = allow_metadata_inventory_move,
458 allow_metadata_inventory_take = allow_metadata_inventory_take,
459 on_receive_fields = receive_fields,
460 _mcl_blast_resistance = 3.5,
461 _mcl_hardness = 3.5,
462 on_rotate = on_rotate,
465 minetest.register_node("mcl_furnaces:furnace_active", {
466 description = S("Burning Furnace"),
467 _doc_items_create_entry = false,
468 tiles = {
469 "default_furnace_top.png", "default_furnace_bottom.png",
470 "default_furnace_side.png", "default_furnace_side.png",
471 "default_furnace_side.png", "default_furnace_front_active.png",
473 paramtype2 = "facedir",
474 paramtype = "light",
475 light_source = LIGHT_ACTIVE_FURNACE,
476 drop = "mcl_furnaces:furnace",
477 groups = {pickaxey=1, container=4, deco_block=1, not_in_creative_inventory=1, material_stone=1},
478 is_ground_content = false,
479 sounds = mcl_sounds.node_sound_stone_defaults(),
480 on_timer = furnace_node_timer,
482 after_dig_node = function(pos, oldnode, oldmetadata, digger)
483 local meta = minetest.get_meta(pos)
484 local meta2 = meta
485 meta:from_table(oldmetadata)
486 local inv = meta:get_inventory()
487 for _, listname in ipairs({"src", "dst", "fuel"}) do
488 local stack = inv:get_stack(listname, 1)
489 if not stack:is_empty() then
490 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}
491 minetest.add_item(p, stack)
494 meta:from_table(meta2:to_table())
495 end,
497 on_construct = function(pos)
498 local node = minetest.get_node(pos)
499 spawn_flames(pos, node.param2)
500 end,
501 on_destruct = function(pos)
502 mcl_particles.delete_node_particlespawners(pos)
503 end,
505 allow_metadata_inventory_put = allow_metadata_inventory_put,
506 allow_metadata_inventory_move = allow_metadata_inventory_move,
507 allow_metadata_inventory_take = allow_metadata_inventory_take,
508 on_metadata_inventory_take = on_metadata_inventory_take,
509 on_receive_fields = receive_fields,
510 _mcl_blast_resistance = 3.5,
511 _mcl_hardness = 3.5,
512 on_rotate = on_rotate,
513 after_rotate = after_rotate_active,
516 minetest.register_craft({
517 output = "mcl_furnaces:furnace",
518 recipe = {
519 { "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble" },
520 { "mcl_core:cobble", "", "mcl_core:cobble" },
521 { "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble" },
525 -- Add entry alias for the Help
526 if minetest.get_modpath("doc") then
527 doc.add_entry_alias("nodes", "mcl_furnaces:furnace", "nodes", "mcl_furnaces:furnace_active")
530 minetest.register_lbm({
531 label = "Active furnace flame particles",
532 name = "mcl_furnaces:flames",
533 nodenames = {"mcl_furnaces:furnace_active"},
534 run_at_every_load = true,
535 action = function(pos, node)
536 spawn_flames(pos, node.param2)
537 end,
540 -- Legacy
541 minetest.register_lbm({
542 label = "Update furnace formspecs (0.60.0",
543 name = "mcl_furnaces:update_formspecs_0_60_0",
544 -- Only update inactive furnaces because active ones should update themselves
545 nodenames = { "mcl_furnaces:furnace" },
546 run_at_every_load = false,
547 action = function(pos, node)
548 local meta = minetest.get_meta(pos)
549 meta:set_string("formspec", inactive_formspec)
550 end,