1 -- mods/default/nodes.lua
5 if (minetest
.get_modpath("intllib")) then
8 return minetest
.formspec_escape(S(s
))
11 S
= function ( s
) return s
end
12 F
= function ( s
) return minetest
.formspec_escape(s
) end
15 minetest
.register_node("default:stone", {
16 description
= S("stone"),
17 tiles
= {"default_stone.png"},
18 is_ground_content
= true,
19 groups
= {cracky
=3, stone
=1},
20 drop
= 'default:cobble',
21 legacy_mineral
= true,
22 sounds
= default
.node_sound_stone_defaults(),
25 minetest
.register_node("default:stone_with_coal", {
26 description
= S("coal ore"),
27 tiles
= {"default_stone.png^default_mineral_coal.png"},
28 is_ground_content
= true,
30 drop
= 'default:coal_lump',
31 sounds
= default
.node_sound_stone_defaults(),
34 minetest
.register_node("default:stone_with_iron", {
35 description
= S("iron ore"),
36 tiles
= {"default_stone.png^default_mineral_iron.png"},
37 is_ground_content
= true,
39 drop
= 'default:iron_lump',
40 sounds
= default
.node_sound_stone_defaults(),
43 minetest
.register_node("default:stone_with_gold", {
44 description
= S("gold ore"),
45 tiles
= {"default_stone.png^default_mineral_gold.png"},
46 is_ground_content
= true,
48 drop
= "default:gold_lump",
49 sounds
= default
.node_sound_stone_defaults(),
52 minetest
.register_node("default:stone_with_diamond", {
53 description
= S("diamond ore"),
54 tiles
= {"default_stone.png^default_mineral_diamond.png"},
55 is_ground_content
= true,
57 drop
= "default:diamond",
58 sounds
= default
.node_sound_stone_defaults(),
61 minetest
.register_node("default:dirt_with_grass", {
62 description
= S("dirt with grass"),
63 tiles
= {"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
64 is_ground_content
= true,
65 groups
= {creative_breakable
=1},
66 drop
= 'default:dirt',
67 sounds
= default
.node_sound_dirt_defaults({
68 footstep
= {name
="default_grass_footstep", gain
=0.25},
72 minetest
.register_node("default:dirt", {
73 description
= S("dirt"),
74 tiles
= {"default_dirt.png"},
75 is_ground_content
= true,
76 groups
= {creative_breakable
=1},
77 sounds
= default
.node_sound_dirt_defaults(),
80 minetest
.register_node("default:sand", {
81 description
= S("sand"),
82 tiles
= {"default_sand.png"},
83 is_ground_content
= true,
84 groups
= {creative_breakable
=1, falling_node
=1},
85 sounds
= default
.node_sound_sand_defaults(),
88 minetest
.register_node("default:tree", {
89 description
= S("tree trunk"),
90 tiles
= {"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
91 paramtype2
= "facedir",
92 is_ground_content
= false,
93 groups
= {creative_breakable
=1},
94 sounds
= default
.node_sound_wood_defaults(),
95 on_place
= minetest
.rotate_node
98 minetest
.register_node("default:leaves", {
99 description
= S("leaves"),
100 drawtype
= "allfaces_optional",
103 tiles
= {"default_leaves.png"},
105 is_ground_content
= false,
106 groups
= {creative_breakable
=1},
111 -- player will get sapling with 1/20 chance
112 items
= {'default:sapling'},
116 -- player will get leaves only if he get no saplings,
117 -- this is because max_items is 1
118 items
= {'default:leaves'},
122 sounds
= default
.node_sound_leaves_defaults(),
125 minetest
.register_node("default:grass_5", {
126 description
= S("grass"),
127 tiles
= {"default_grass_5.png"},
128 is_ground_content
= true,
129 groups
= {attached_node
=1,creative_breakable
=1},
130 sounds
= default
.node_sound_leaves_defaults(),
131 wield_image
= "default_grass_5.png",
132 inventory_image
= "default_grass_5.png",
133 drawtype
= "plantlike",
134 sunlight_propagates
= true,
139 minetest
.register_node("default:ladder", {
140 description
= S("ladder"),
141 drawtype
= "signlike",
142 tiles
= {"default_ladder.png"},
143 inventory_image
= "default_ladder.png",
144 wield_image
= "default_ladder.png",
146 paramtype2
= "wallmounted",
149 is_ground_content
= false,
151 type = "wallmounted",
153 groups
= {attached_node
=1,creative_breakable
=1},
154 legacy_wallmounted
= true,
155 sounds
= default
.node_sound_wood_defaults(),
158 minetest
.register_node("default:wood", {
159 description
= S("wooden planks"),
160 tiles
= {"default_wood.png"},
161 groups
= {choppy
=2,wood
=1},
162 sounds
= default
.node_sound_wood_defaults(),
165 minetest
.register_node("default:water_flowing", {
166 description
= S("flowing water"),
167 inventory_image
= minetest
.inventorycube("default_water.png"),
168 drawtype
= "flowingliquid",
169 tiles
= {"default_water.png"},
172 image
="default_water_flowing_animated.png",
173 backface_culling
=false,
174 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
177 image
="default_water_flowing_animated.png",
178 backface_culling
=true,
179 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
184 paramtype2
= "flowingliquid",
191 liquidtype
= "flowing",
192 liquid_alternative_flowing
= "default:water_flowing",
193 liquid_alternative_source
= "default:water_source",
194 liquid_viscosity
= WATER_VISC
,
195 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
196 groups
= {water
=3, liquid
=3, puts_out_fire
=1, not_in_creative_inventory
=1, freezes
=1, melt_around
=1},
197 sounds
= default
.node_sound_water_defaults(),
200 minetest
.register_node("default:water_source", {
201 description
= S("water source"),
202 inventory_image
= minetest
.inventorycube("default_water.png"),
205 {name
="default_water_source_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0}}
208 -- New-style water source material (mostly unused)
210 name
="default_water_source_animated.png",
211 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0},
212 backface_culling
= false,
223 liquidtype
= "source",
224 liquid_alternative_flowing
= "default:water_flowing",
225 liquid_alternative_source
= "default:water_source",
226 liquid_viscosity
= WATER_VISC
,
227 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
228 groups
= {water
=3, liquid
=3, puts_out_fire
=1, freezes
=1},
229 sounds
= default
.node_sound_water_defaults(),
232 minetest
.register_node("default:torch", {
233 description
= S("torch"),
234 drawtype
= "torchlike",
235 --tiles = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
237 {name
="default_torch_on_floor_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
238 {name
="default_torch_on_ceiling_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
239 {name
="default_torch_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}}
241 inventory_image
= "default_torch_on_floor.png",
242 wield_image
= "default_torch_on_floor.png",
244 paramtype2
= "wallmounted",
245 sunlight_propagates
= true,
246 is_ground_content
= false,
248 light_source
= LIGHT_MAX
-1,
250 type = "wallmounted",
251 wall_top
= {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
252 wall_bottom
= {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
253 wall_side
= {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
255 groups
= {attached_node
=1,creative_breakable
= 1},
256 legacy_wallmounted
= true,
257 sounds
= default
.node_sound_defaults(),
260 function default
.chest_formspec()
266 "label[0,-0.2;"..minetest
.formspec_escape(S("Chest inventory:")).."]"..
267 "list[current_name;main;0,0.3;8,4;]"..
268 "label[0,4.35;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
269 "list[current_player;main;0,4.85;8,1;]"..
270 "list[current_player;main;0,6.08;8,3;8]"..
271 "listring[current_name;main]"..
272 "listring[current_player;main]"..
273 "label[0,9.1;"..default
.gui_controls
.."]"..
274 default
.get_hotbar_bg(0,4.85)
277 minetest
.register_node("default:chest", {
278 description
= S("storage chest"),
279 tiles
= {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
280 "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
281 paramtype2
= "facedir",
282 groups
= {creative_breakable
=1},
283 legacy_facedir_simple
= true,
284 is_ground_content
= false,
285 sounds
= default
.node_sound_wood_defaults(),
286 on_construct
= function(pos
)
287 local meta
= minetest
.get_meta(pos
)
288 meta
:set_string("formspec", default
.chest_formspec())
289 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
290 local inv
= meta
:get_inventory()
291 inv
:set_size("main", 8*4)
293 can_dig
= function(pos
,player
)
294 local meta
= minetest
.get_meta(pos
);
295 local inv
= meta
:get_inventory()
296 return inv
:is_empty("main")
298 on_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
299 minetest
.log("action", player
:get_player_name()..
300 " moves stuff in chest at "..minetest
.pos_to_string(pos
))
302 on_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
303 minetest
.log("action", player
:get_player_name()..
304 " moves stuff to chest at "..minetest
.pos_to_string(pos
))
306 on_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
307 minetest
.log("action", player
:get_player_name()..
308 " takes stuff from chest at "..minetest
.pos_to_string(pos
))
312 function default
.furnace_active(pos
, percent
, item_percent
)
318 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is active and constantly burning its fuel.")).."]"..
319 "label[2.25,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
320 "list[current_name;src;2.25,0.5;1,1;]"..
321 "label[2.25,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
322 "list[current_name;fuel;2.25,2.9;1,1;]"..
323 "label[2.25,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
324 "image[2.25,1.7;1,1;default_furnace_fire_bg.png^[lowpart:"..
325 (100-percent
)..":default_furnace_fire_fg.png]"..
326 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
327 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[lowpart:"..
328 (item_percent
*100)..":gui_furnace_arrow_fg.png^[transformR270]"..
329 "label[5.75,0.70;"..minetest
.formspec_escape(S("Output slots:")).."]"..
330 "list[current_name;dst;5.75,1.16;2,2;]"..
331 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
332 "list[current_player;main;0,4.25;8,1;]"..
333 "list[current_player;main;0,5.5;8,3;8]"..
334 "listring[current_name;dst]"..
335 "listring[current_player;main]"..
336 "listring[current_name;src]"..
337 "listring[current_player;main]"..
338 "label[0,8.5;"..default
.gui_controls
.."]"..
339 default
.get_hotbar_bg(0,4.25)
343 function default
.get_furnace_active_formspec(pos
, percent
)
344 local meta
= minetest
.get_meta(pos
)local inv
= meta
:get_inventory()
345 local srclist
= inv
:get_list("src")
347 local aftercooked
= nil
349 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
351 local item_percent
= 0
353 item_percent
= meta
:get_float("src_time")/cooked
.time
356 return default
.furnace_active(pos
, percent
, item_percent
)
359 default
.furnace_inactive_formspec
=
364 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is inactive. Please read the sign above.")).."]"..
365 "label[2.25,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
366 "list[current_name;src;2.25,0.5;1,1;]"..
367 "label[2.25,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
368 "list[current_name;fuel;2.25,2.9;1,1;]"..
369 "label[2.25,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
370 "image[2.25,1.7;1,1;default_furnace_fire_bg.png]"..
371 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
372 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
373 "label[5.75,0.70;"..minetest
.formspec_escape(S("Output slots:")).."]"..
374 "list[current_name;dst;5.75,1.16;2,2;]"..
375 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
376 "list[current_player;main;0,4.25;8,1;]"..
377 "list[current_player;main;0,5.5;8,3;8]"..
378 "listring[current_name;dst]"..
379 "listring[current_player;main]"..
380 "listring[current_name;src]"..
381 "listring[current_player;main]"..
382 "label[0,8.5;"..default
.gui_controls
.."]"..
383 default
.get_hotbar_bg(0,4.25)
385 minetest
.register_node("default:furnace", {
386 description
= S("furnace"),
387 tiles
= {"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_side.png",
388 "default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
389 paramtype2
= "facedir",
390 groups
= {creative_breakable
=1},
391 legacy_facedir_simple
= true,
392 is_ground_content
= false,
393 sounds
= default
.node_sound_stone_defaults(),
394 on_construct
= function(pos
)
395 local meta
= minetest
.get_meta(pos
)
396 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
397 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"))
398 local inv
= meta
:get_inventory()
399 inv
:set_size("fuel", 1)
400 inv
:set_size("src", 1)
401 inv
:set_size("dst", 4)
403 can_dig
= function(pos
,player
)
404 local meta
= minetest
.get_meta(pos
);
405 local inv
= meta
:get_inventory()
406 if not inv
:is_empty("fuel") then
408 elseif not inv
:is_empty("dst") then
410 elseif not inv
:is_empty("src") then
415 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
416 if minetest
.is_protected(pos
, player
:get_player_name()) then
419 local meta
= minetest
.get_meta(pos
)
420 local inv
= meta
:get_inventory()
421 if listname
== "fuel" then
422 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
423 if inv
:is_empty("src") then
424 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
426 return stack
:get_count()
430 elseif listname
== "src" then
431 return stack
:get_count()
432 elseif listname
== "dst" then
436 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
437 if minetest
.is_protected(pos
, player
:get_player_name()) then
440 local meta
= minetest
.get_meta(pos
)
441 local inv
= meta
:get_inventory()
442 local stack
= inv
:get_stack(from_list
, from_index
)
443 if to_list
== "fuel" then
444 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
445 if inv
:is_empty("src") then
446 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
452 elseif to_list
== "src" then
454 elseif to_list
== "dst" then
458 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
459 if minetest
.is_protected(pos
, player
:get_player_name()) then
462 return stack
:get_count()
466 minetest
.register_node("default:furnace_active", {
467 description
= S("furnace"),
469 "default_furnace_top.png",
470 "default_furnace_bottom.png",
471 "default_furnace_side.png",
472 "default_furnace_side.png",
473 "default_furnace_side.png",
475 image
= "default_furnace_front_active.png",
476 backface_culling
= false,
478 type = "vertical_frames",
485 paramtype2
= "facedir",
487 drop
= "default:furnace",
488 groups
= {creative_breakable
=1,not_in_creative_inventory
=1,hot
=1},
489 legacy_facedir_simple
= true,
490 is_ground_content
= false,
491 sounds
= default
.node_sound_stone_defaults(),
492 on_construct
= function(pos
)
493 local meta
= minetest
.get_meta(pos
)
494 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
495 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"));
496 local inv
= meta
:get_inventory()
497 inv
:set_size("fuel", 1)
498 inv
:set_size("src", 1)
499 inv
:set_size("dst", 4)
501 can_dig
= function(pos
,player
)
502 local meta
= minetest
.get_meta(pos
);
503 local inv
= meta
:get_inventory()
504 if not inv
:is_empty("fuel") then
506 elseif not inv
:is_empty("dst") then
508 elseif not inv
:is_empty("src") then
513 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
514 if minetest
.is_protected(pos
, player
:get_player_name()) then
517 local meta
= minetest
.get_meta(pos
)
518 local inv
= meta
:get_inventory()
519 if listname
== "fuel" then
520 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
521 if inv
:is_empty("src") then
522 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
524 return stack
:get_count()
528 elseif listname
== "src" then
529 return stack
:get_count()
530 elseif listname
== "dst" then
534 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
535 if minetest
.is_protected(pos
, player
:get_player_name()) then
538 local meta
= minetest
.get_meta(pos
)
539 local inv
= meta
:get_inventory()
540 local stack
= inv
:get_stack(from_list
, from_index
)
541 if to_list
== "fuel" then
542 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
543 if inv
:is_empty("src") then
544 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
550 elseif to_list
== "src" then
552 elseif to_list
== "dst" then
556 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
557 if minetest
.is_protected(pos
, player
:get_player_name()) then
560 return stack
:get_count()
564 local function swap_node(pos
,name
)
565 local node
= minetest
.get_node(pos
)
566 if node
.name
== name
then
570 minetest
.swap_node(pos
,node
)
573 minetest
.register_abm({
574 nodenames
= {"default:chest"},
577 action
= function(pos
,node
,active_object_count
, active_object_count_wider
)
578 local meta
= minetest
.get_meta(pos
)
579 meta
:set_string("formspec", default
.chest_formspec())
580 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
584 minetest
.register_abm({
585 nodenames
= {"default:furnace","default:furnace_active"},
588 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
589 local meta
= minetest
.get_meta(pos
)
590 for i
, name
in ipairs({
596 if meta
:get_string(name
) == "" then
597 meta
:set_float(name
, 0.0)
601 local inv
= meta
:get_inventory()
603 local srclist
= inv
:get_list("src")
608 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
611 local was_active
= false
613 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
615 meta
:set_float("fuel_time", meta
:get_float("fuel_time") + 1)
616 meta
:set_float("src_time", meta
:get_float("src_time") + 1)
617 if cooked
and cooked
.item
and meta
:get_float("src_time") >= cooked
.time
then
618 -- check if there's room for output in "dst" list
619 if inv
:room_for_item("dst",cooked
.item
) then
620 -- Put result in "dst" list
621 inv
:add_item("dst", cooked
.item
)
622 -- take stuff from "src" list
623 inv
:set_stack("src", 1, aftercooked
.items
[1])
625 --print("Could not insert '"..cooked.item:to_string().."'")
627 meta
:set_string("src_time", 0)
631 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
632 local percent
= math
.floor(meta
:get_float("fuel_time") /
633 meta
:get_float("fuel_totaltime") * 100)
634 meta
:set_string("infotext",string.format(S("Active furnace (Flame used: %d%%) (Rightclick to examine)"), percent
))
635 swap_node(pos
,"default:furnace_active")
636 meta
:set_string("formspec",default
.get_furnace_active_formspec(pos
, percent
))
643 local fuellist
= inv
:get_list("fuel")
644 local srclist
= inv
:get_list("src")
647 cooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
650 fuel
, afterfuel
= minetest
.get_craft_result({method
= "fuel", width
= 1, items
= fuellist
})
653 if not fuel
or fuel
.time
<= 0 then
654 meta
:set_string("infotext",S("Furnace without fuel (Rightclick to examine)"))
655 swap_node(pos
,"default:furnace")
656 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
660 if cooked
.item
:is_empty() then
662 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
663 swap_node(pos
,"default:furnace")
664 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
669 meta
:set_string("fuel_totaltime", fuel
.time
)
670 meta
:set_string("fuel_time", 0)
672 inv
:set_stack("fuel", 1, afterfuel
.items
[1])
676 minetest
.register_node("default:cobble", {
677 description
= S("cobblestone"),
678 tiles
= {"default_cobble.png"},
679 is_ground_content
= true,
680 groups
= {cracky
=3, stone
=2},
681 sounds
= default
.node_sound_stone_defaults(),
684 minetest
.register_node("default:apple", {
685 description
= S("apple"),
686 drawtype
= "plantlike",
688 tiles
= {"default_apple.png"},
689 inventory_image
= "default_apple.png",
691 sunlight_propagates
= true,
693 is_ground_content
= true,
696 fixed
= {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
698 groups
= {dig_immediate
=3,},
699 on_use
= minetest
.item_eat(1),
700 sounds
= default
.node_sound_leaves_defaults(),
701 after_place_node
= function(pos
, placer
, itemstack
)
702 if placer
:is_player() then
703 minetest
.set_node(pos
, {name
="default:apple", param2
=1})