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
= {immortal
=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
= {immortal
=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
= {crumbly
=3, falling_node
=1, sand
=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
= {immortal
=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
= {immortal
=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:ladder", {
126 description
= S("ladder"),
127 drawtype
= "signlike",
128 tiles
= {"default_ladder.png"},
129 inventory_image
= "default_ladder.png",
130 wield_image
= "default_ladder.png",
132 paramtype2
= "wallmounted",
135 is_ground_content
= false,
137 type = "wallmounted",
140 legacy_wallmounted
= true,
141 sounds
= default
.node_sound_wood_defaults(),
144 minetest
.register_node("default:wood", {
145 description
= S("wooden planks"),
146 tiles
= {"default_wood.png"},
147 groups
= {choppy
=2,wood
=1},
148 sounds
= default
.node_sound_wood_defaults(),
151 minetest
.register_node("default:water_flowing", {
152 description
= S("flowing water"),
153 inventory_image
= minetest
.inventorycube("default_water.png"),
154 drawtype
= "flowingliquid",
155 tiles
= {"default_water.png"},
158 image
="default_water_flowing_animated.png",
159 backface_culling
=false,
160 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
163 image
="default_water_flowing_animated.png",
164 backface_culling
=true,
165 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
170 paramtype2
= "flowingliquid",
177 liquidtype
= "flowing",
178 liquid_alternative_flowing
= "default:water_flowing",
179 liquid_alternative_source
= "default:water_source",
180 liquid_viscosity
= WATER_VISC
,
181 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
182 groups
= {water
=3, liquid
=3, puts_out_fire
=1, not_in_creative_inventory
=1, freezes
=1, melt_around
=1},
185 minetest
.register_node("default:water_source", {
186 description
= S("water source"),
187 inventory_image
= minetest
.inventorycube("default_water.png"),
190 {name
="default_water_source_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0}}
193 -- New-style water source material (mostly unused)
195 name
="default_water_source_animated.png",
196 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0},
197 backface_culling
= false,
208 liquidtype
= "source",
209 liquid_alternative_flowing
= "default:water_flowing",
210 liquid_alternative_source
= "default:water_source",
211 liquid_viscosity
= WATER_VISC
,
212 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
213 groups
= {water
=3, liquid
=3, puts_out_fire
=1, freezes
=1},
216 minetest
.register_node("default:torch", {
217 description
= S("torch"),
218 drawtype
= "torchlike",
219 --tiles = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
221 {name
="default_torch_on_floor_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
222 {name
="default_torch_on_ceiling_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
223 {name
="default_torch_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}}
225 inventory_image
= "default_torch_on_floor.png",
226 wield_image
= "default_torch_on_floor.png",
228 paramtype2
= "wallmounted",
229 sunlight_propagates
= true,
230 is_ground_content
= false,
232 light_source
= LIGHT_MAX
-1,
234 type = "wallmounted",
235 wall_top
= {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
236 wall_bottom
= {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
237 wall_side
= {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
239 groups
= {immortal
= 1},
240 legacy_wallmounted
= true,
241 sounds
= default
.node_sound_defaults(),
244 function default
.chest_formspec()
250 "label[0,-0.2;"..minetest
.formspec_escape(S("Chest inventory:")).."]"..
251 "list[current_name;main;0,0.3;8,4;]"..
252 "label[0,4.35;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
253 "list[current_player;main;0,4.85;8,1;]"..
254 "list[current_player;main;0,6.08;8,3;8]"..
255 "listring[current_name;main]"..
256 "listring[current_player;main]"..
257 "label[0,9.1;"..default
.gui_controls
.."]"..
258 default
.get_hotbar_bg(0,4.85)
261 minetest
.register_node("default:chest", {
262 description
= S("storage chest"),
263 tiles
= {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
264 "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
265 paramtype2
= "facedir",
266 groups
= {immortal
=1},
267 legacy_facedir_simple
= true,
268 is_ground_content
= false,
269 sounds
= default
.node_sound_wood_defaults(),
270 on_construct
= function(pos
)
271 local meta
= minetest
.get_meta(pos
)
272 meta
:set_string("formspec", default
.chest_formspec())
273 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
274 local inv
= meta
:get_inventory()
275 inv
:set_size("main", 8*4)
277 can_dig
= function(pos
,player
)
278 local meta
= minetest
.get_meta(pos
);
279 local inv
= meta
:get_inventory()
280 return inv
:is_empty("main")
282 on_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
283 minetest
.log("action", player
:get_player_name()..
284 " moves stuff in chest at "..minetest
.pos_to_string(pos
))
286 on_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
287 minetest
.log("action", player
:get_player_name()..
288 " moves stuff to chest at "..minetest
.pos_to_string(pos
))
290 on_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
291 minetest
.log("action", player
:get_player_name()..
292 " takes stuff from chest at "..minetest
.pos_to_string(pos
))
296 function default
.furnace_active(pos
, percent
, item_percent
)
302 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is active and constantly burning its fuel.")).."]"..
303 "label[2.25,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
304 "list[current_name;src;2.25,0.5;1,1;]"..
305 "label[2.25,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
306 "list[current_name;fuel;2.25,2.9;1,1;]"..
307 "label[2.25,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
308 "image[2.25,1.7;1,1;default_furnace_fire_bg.png^[lowpart:"..
309 (100-percent
)..":default_furnace_fire_fg.png]"..
310 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
311 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[lowpart:"..
312 (item_percent
*100)..":gui_furnace_arrow_fg.png^[transformR270]"..
313 "label[5.75,0.70;"..minetest
.formspec_escape(S("Output slots:")).."]"..
314 "list[current_name;dst;5.75,1.16;2,2;]"..
315 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
316 "list[current_player;main;0,4.25;8,1;]"..
317 "list[current_player;main;0,5.5;8,3;8]"..
318 "listring[current_name;dst]"..
319 "listring[current_player;main]"..
320 "listring[current_name;src]"..
321 "listring[current_player;main]"..
322 "label[0,8.5;"..default
.gui_controls
.."]"..
323 default
.get_hotbar_bg(0,4.25)
327 function default
.get_furnace_active_formspec(pos
, percent
)
328 local meta
= minetest
.get_meta(pos
)local inv
= meta
:get_inventory()
329 local srclist
= inv
:get_list("src")
331 local aftercooked
= nil
333 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
335 local item_percent
= 0
337 item_percent
= meta
:get_float("src_time")/cooked
.time
340 return default
.furnace_active(pos
, percent
, item_percent
)
343 default
.furnace_inactive_formspec
=
348 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is inactive. Please read the sign above.")).."]"..
349 "label[2.25,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
350 "list[current_name;src;2.25,0.5;1,1;]"..
351 "label[2.25,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
352 "list[current_name;fuel;2.25,2.9;1,1;]"..
353 "label[2.25,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
354 "image[2.25,1.7;1,1;default_furnace_fire_bg.png]"..
355 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
356 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
357 "label[5.75,0.70;"..minetest
.formspec_escape(S("Output slots:")).."]"..
358 "list[current_name;dst;5.75,1.16;2,2;]"..
359 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
360 "list[current_player;main;0,4.25;8,1;]"..
361 "list[current_player;main;0,5.5;8,3;8]"..
362 "listring[current_name;dst]"..
363 "listring[current_player;main]"..
364 "listring[current_name;src]"..
365 "listring[current_player;main]"..
366 "label[0,8.5;"..default
.gui_controls
.."]"..
367 default
.get_hotbar_bg(0,4.25)
369 minetest
.register_node("default:furnace", {
370 description
= S("furnace"),
371 tiles
= {"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_side.png",
372 "default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
373 paramtype2
= "facedir",
374 groups
= {immortal
=1},
375 legacy_facedir_simple
= true,
376 is_ground_content
= false,
377 sounds
= default
.node_sound_stone_defaults(),
378 on_construct
= function(pos
)
379 local meta
= minetest
.get_meta(pos
)
380 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
381 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"))
382 local inv
= meta
:get_inventory()
383 inv
:set_size("fuel", 1)
384 inv
:set_size("src", 1)
385 inv
:set_size("dst", 4)
387 can_dig
= function(pos
,player
)
388 local meta
= minetest
.get_meta(pos
);
389 local inv
= meta
:get_inventory()
390 if not inv
:is_empty("fuel") then
392 elseif not inv
:is_empty("dst") then
394 elseif not inv
:is_empty("src") then
399 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
400 if minetest
.is_protected(pos
, player
:get_player_name()) then
403 local meta
= minetest
.get_meta(pos
)
404 local inv
= meta
:get_inventory()
405 if listname
== "fuel" then
406 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
407 if inv
:is_empty("src") then
408 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
410 return stack
:get_count()
414 elseif listname
== "src" then
415 return stack
:get_count()
416 elseif listname
== "dst" then
420 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
421 if minetest
.is_protected(pos
, player
:get_player_name()) then
424 local meta
= minetest
.get_meta(pos
)
425 local inv
= meta
:get_inventory()
426 local stack
= inv
:get_stack(from_list
, from_index
)
427 if to_list
== "fuel" then
428 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
429 if inv
:is_empty("src") then
430 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
436 elseif to_list
== "src" then
438 elseif to_list
== "dst" then
442 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
443 if minetest
.is_protected(pos
, player
:get_player_name()) then
446 return stack
:get_count()
450 minetest
.register_node("default:furnace_active", {
451 description
= S("furnace"),
453 "default_furnace_top.png",
454 "default_furnace_bottom.png",
455 "default_furnace_side.png",
456 "default_furnace_side.png",
457 "default_furnace_side.png",
459 image
= "default_furnace_front_active.png",
460 backface_culling
= false,
462 type = "vertical_frames",
469 paramtype2
= "facedir",
471 drop
= "default:furnace",
472 groups
= {immortal
=1,not_in_creative_inventory
=1,hot
=1},
473 legacy_facedir_simple
= true,
474 is_ground_content
= false,
475 sounds
= default
.node_sound_stone_defaults(),
476 on_construct
= function(pos
)
477 local meta
= minetest
.get_meta(pos
)
478 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
479 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"));
480 local inv
= meta
:get_inventory()
481 inv
:set_size("fuel", 1)
482 inv
:set_size("src", 1)
483 inv
:set_size("dst", 4)
485 can_dig
= function(pos
,player
)
486 local meta
= minetest
.get_meta(pos
);
487 local inv
= meta
:get_inventory()
488 if not inv
:is_empty("fuel") then
490 elseif not inv
:is_empty("dst") then
492 elseif not inv
:is_empty("src") then
497 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
498 if minetest
.is_protected(pos
, player
:get_player_name()) then
501 local meta
= minetest
.get_meta(pos
)
502 local inv
= meta
:get_inventory()
503 if listname
== "fuel" then
504 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
505 if inv
:is_empty("src") then
506 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
508 return stack
:get_count()
512 elseif listname
== "src" then
513 return stack
:get_count()
514 elseif listname
== "dst" then
518 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
519 if minetest
.is_protected(pos
, player
:get_player_name()) then
522 local meta
= minetest
.get_meta(pos
)
523 local inv
= meta
:get_inventory()
524 local stack
= inv
:get_stack(from_list
, from_index
)
525 if to_list
== "fuel" then
526 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
527 if inv
:is_empty("src") then
528 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
534 elseif to_list
== "src" then
536 elseif to_list
== "dst" then
540 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
541 if minetest
.is_protected(pos
, player
:get_player_name()) then
544 return stack
:get_count()
548 local function swap_node(pos
,name
)
549 local node
= minetest
.get_node(pos
)
550 if node
.name
== name
then
554 minetest
.swap_node(pos
,node
)
557 minetest
.register_abm({
558 nodenames
= {"default:chest"},
561 action
= function(pos
,node
,active_object_count
, active_object_count_wider
)
562 local meta
= minetest
.get_meta(pos
)
563 meta
:set_string("formspec", default
.chest_formspec())
564 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
568 minetest
.register_abm({
569 nodenames
= {"default:furnace","default:furnace_active"},
572 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
573 local meta
= minetest
.get_meta(pos
)
574 for i
, name
in ipairs({
580 if meta
:get_string(name
) == "" then
581 meta
:set_float(name
, 0.0)
585 local inv
= meta
:get_inventory()
587 local srclist
= inv
:get_list("src")
592 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
595 local was_active
= false
597 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
599 meta
:set_float("fuel_time", meta
:get_float("fuel_time") + 1)
600 meta
:set_float("src_time", meta
:get_float("src_time") + 1)
601 if cooked
and cooked
.item
and meta
:get_float("src_time") >= cooked
.time
then
602 -- check if there's room for output in "dst" list
603 if inv
:room_for_item("dst",cooked
.item
) then
604 -- Put result in "dst" list
605 inv
:add_item("dst", cooked
.item
)
606 -- take stuff from "src" list
607 inv
:set_stack("src", 1, aftercooked
.items
[1])
609 --print("Could not insert '"..cooked.item:to_string().."'")
611 meta
:set_string("src_time", 0)
615 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
616 local percent
= math
.floor(meta
:get_float("fuel_time") /
617 meta
:get_float("fuel_totaltime") * 100)
618 meta
:set_string("infotext",string.format(S("Active furnace (Flame used: %d%%) (Rightclick to examine)"), percent
))
619 swap_node(pos
,"default:furnace_active")
620 meta
:set_string("formspec",default
.get_furnace_active_formspec(pos
, percent
))
627 local fuellist
= inv
:get_list("fuel")
628 local srclist
= inv
:get_list("src")
631 cooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
634 fuel
, afterfuel
= minetest
.get_craft_result({method
= "fuel", width
= 1, items
= fuellist
})
637 if not fuel
or fuel
.time
<= 0 then
638 meta
:set_string("infotext",S("Furnace without fuel (Rightclick to examine)"))
639 swap_node(pos
,"default:furnace")
640 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
644 if cooked
.item
:is_empty() then
646 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
647 swap_node(pos
,"default:furnace")
648 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
653 meta
:set_string("fuel_totaltime", fuel
.time
)
654 meta
:set_string("fuel_time", 0)
656 inv
:set_stack("fuel", 1, afterfuel
.items
[1])
660 minetest
.register_node("default:cobble", {
661 description
= S("cobblestone"),
662 tiles
= {"default_cobble.png"},
663 is_ground_content
= true,
664 groups
= {cracky
=3, stone
=2},
665 sounds
= default
.node_sound_stone_defaults(),
668 minetest
.register_node("default:apple", {
669 description
= S("apple"),
670 drawtype
= "plantlike",
672 tiles
= {"default_apple.png"},
673 inventory_image
= "default_apple.png",
675 sunlight_propagates
= true,
677 is_ground_content
= true,
680 fixed
= {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
682 groups
= {dig_immediate
=3,},
683 on_use
= minetest
.item_eat(1),
684 sounds
= default
.node_sound_leaves_defaults(),
685 after_place_node
= function(pos
, placer
, itemstack
)
686 if placer
:is_player() then
687 minetest
.set_node(pos
, {name
="default:apple", param2
=1})