1 -- mods/default/nodes.lua
5 if (minetest
.get_modpath("intllib")) then
6 dofile(minetest
.get_modpath("intllib").."/intllib.lua")
7 S
= intllib
.Getter(minetest
.get_current_modname())
9 return minetest
.formspec_escape(S(s
))
12 S
= function ( s
) return s
end
13 F
= function ( s
) return minetest
.formspec_escape(s
) end
16 minetest
.register_node("default:stone", {
17 description
= S("stone"),
18 tiles
= {"default_stone.png"},
19 is_ground_content
= true,
20 groups
= {cracky
=3, stone
=1},
21 drop
= 'default:cobble',
22 legacy_mineral
= true,
23 sounds
= default
.node_sound_stone_defaults(),
26 minetest
.register_node("default:stone_with_coal", {
27 description
= S("coal ore"),
28 tiles
= {"default_stone.png^default_mineral_coal.png"},
29 is_ground_content
= true,
31 drop
= 'default:coal_lump',
32 sounds
= default
.node_sound_stone_defaults(),
35 minetest
.register_node("default:stone_with_iron", {
36 description
= S("iron ore"),
37 tiles
= {"default_stone.png^default_mineral_iron.png"},
38 is_ground_content
= true,
40 drop
= 'default:iron_lump',
41 sounds
= default
.node_sound_stone_defaults(),
44 minetest
.register_node("default:stone_with_gold", {
45 description
= S("gold ore"),
46 tiles
= {"default_stone.png^default_mineral_gold.png"},
47 is_ground_content
= true,
49 drop
= "default:gold_lump",
50 sounds
= default
.node_sound_stone_defaults(),
53 minetest
.register_node("default:stone_with_diamond", {
54 description
= S("diamond ore"),
55 tiles
= {"default_stone.png^default_mineral_diamond.png"},
56 is_ground_content
= true,
58 drop
= "default:diamond",
59 sounds
= default
.node_sound_stone_defaults(),
62 minetest
.register_node("default:stonebrick", {
63 description
= S("stone brick"),
64 tiles
= {"default_stone_brick.png"},
65 groups
= {cracky
=2, stone
=1},
66 sounds
= default
.node_sound_stone_defaults(),
69 minetest
.register_node("default:dirt_with_grass", {
70 description
= S("dirt with grass"),
71 tiles
= {"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
72 is_ground_content
= true,
73 groups
= {immortal
=1},
74 drop
= 'default:dirt',
75 sounds
= default
.node_sound_dirt_defaults({
76 footstep
= {name
="default_grass_footstep", gain
=0.25},
80 minetest
.register_node("default:dirt", {
81 description
= S("dirt"),
82 tiles
= {"default_dirt.png"},
83 is_ground_content
= true,
84 groups
= {immortal
=1},
85 sounds
= default
.node_sound_dirt_defaults(),
88 minetest
.register_node("default:sand", {
89 description
= S("sand"),
90 tiles
= {"default_sand.png"},
91 is_ground_content
= true,
92 groups
= {crumbly
=3, falling_node
=1, sand
=1},
93 sounds
= default
.node_sound_sand_defaults(),
96 minetest
.register_node("default:tree", {
97 description
= S("tree trunk"),
98 tiles
= {"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
99 paramtype2
= "facedir",
100 is_ground_content
= false,
101 groups
= {immortal
=1},
102 sounds
= default
.node_sound_wood_defaults(),
103 on_place
= minetest
.rotate_node
106 minetest
.register_node("default:leaves", {
107 description
= S("leaves"),
108 drawtype
= "allfaces_optional",
111 tiles
= {"default_leaves.png"},
113 is_ground_content
= false,
114 groups
= {immortal
=1},
119 -- player will get sapling with 1/20 chance
120 items
= {'default:sapling'},
124 -- player will get leaves only if he get no saplings,
125 -- this is because max_items is 1
126 items
= {'default:leaves'},
130 sounds
= default
.node_sound_leaves_defaults(),
133 minetest
.register_node("default:ladder", {
134 description
= S("ladder"),
135 drawtype
= "signlike",
136 tiles
= {"default_ladder.png"},
137 inventory_image
= "default_ladder.png",
138 wield_image
= "default_ladder.png",
140 paramtype2
= "wallmounted",
143 is_ground_content
= false,
145 type = "wallmounted",
147 groups
= {choppy
=2,flammable
=2},
148 legacy_wallmounted
= true,
149 sounds
= default
.node_sound_wood_defaults(),
152 minetest
.register_node("default:wood", {
153 description
= S("wooden planks"),
154 tiles
= {"default_wood.png"},
155 groups
= {choppy
=2,flammable
=3,wood
=1},
156 sounds
= default
.node_sound_wood_defaults(),
159 minetest
.register_node("default:water_flowing", {
160 description
= S("flowing water"),
161 inventory_image
= minetest
.inventorycube("default_water.png"),
162 drawtype
= "flowingliquid",
163 tiles
= {"default_water.png"},
166 image
="default_water_flowing_animated.png",
167 backface_culling
=false,
168 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
171 image
="default_water_flowing_animated.png",
172 backface_culling
=true,
173 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=0.8}
178 paramtype2
= "flowingliquid",
185 liquidtype
= "flowing",
186 liquid_alternative_flowing
= "default:water_flowing",
187 liquid_alternative_source
= "default:water_source",
188 liquid_viscosity
= WATER_VISC
,
189 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
190 groups
= {water
=3, liquid
=3, puts_out_fire
=1, not_in_creative_inventory
=1, freezes
=1, melt_around
=1},
193 minetest
.register_node("default:water_source", {
194 description
= S("water source"),
195 inventory_image
= minetest
.inventorycube("default_water.png"),
198 {name
="default_water_source_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0}}
201 -- New-style water source material (mostly unused)
203 name
="default_water_source_animated.png",
204 animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=2.0},
205 backface_culling
= false,
216 liquidtype
= "source",
217 liquid_alternative_flowing
= "default:water_flowing",
218 liquid_alternative_source
= "default:water_source",
219 liquid_viscosity
= WATER_VISC
,
220 post_effect_color
= {a
=64, r
=100, g
=100, b
=200},
221 groups
= {water
=3, liquid
=3, puts_out_fire
=1, freezes
=1},
224 minetest
.register_node("default:torch", {
225 description
= S("torch"),
226 drawtype
= "torchlike",
227 --tiles = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
229 {name
="default_torch_on_floor_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
230 {name
="default_torch_on_ceiling_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}},
231 {name
="default_torch_animated.png", animation
={type="vertical_frames", aspect_w
=16, aspect_h
=16, length
=3.0}}
233 inventory_image
= "default_torch_on_floor.png",
234 wield_image
= "default_torch_on_floor.png",
236 paramtype2
= "wallmounted",
237 sunlight_propagates
= true,
238 is_ground_content
= false,
240 light_source
= LIGHT_MAX
-1,
242 type = "wallmounted",
243 wall_top
= {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
244 wall_bottom
= {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
245 wall_side
= {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
247 groups
= {immortal
= 1},
248 legacy_wallmounted
= true,
249 sounds
= default
.node_sound_defaults(),
252 function default
.chest_formspec()
258 "label[0,-0.2;"..minetest
.formspec_escape(S("Chest inventory:")).."]"..
259 "list[current_name;main;0,0.3;8,4;]"..
260 "label[0,4.35;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
261 "list[current_player;main;0,4.85;8,1;]"..
262 "list[current_player;main;0,6.08;8,3;8]"..
263 "label[0,9;"..default
.gui_controls
.."]"..
264 default
.get_hotbar_bg(0,4.85)
267 minetest
.register_node("default:chest", {
268 description
= S("storage chest"),
269 tiles
= {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
270 "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
271 paramtype2
= "facedir",
272 groups
= {immortal
=1},
273 legacy_facedir_simple
= true,
274 is_ground_content
= false,
275 sounds
= default
.node_sound_wood_defaults(),
276 on_construct
= function(pos
)
277 local meta
= minetest
.get_meta(pos
)
278 meta
:set_string("formspec", default
.chest_formspec())
279 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
280 local inv
= meta
:get_inventory()
281 inv
:set_size("main", 8*4)
283 can_dig
= function(pos
,player
)
284 local meta
= minetest
.get_meta(pos
);
285 local inv
= meta
:get_inventory()
286 return inv
:is_empty("main")
288 on_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
289 minetest
.log("action", player
:get_player_name()..
290 " moves stuff in chest at "..minetest
.pos_to_string(pos
))
292 on_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
293 minetest
.log("action", player
:get_player_name()..
294 " moves stuff to chest at "..minetest
.pos_to_string(pos
))
296 on_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
297 minetest
.log("action", player
:get_player_name()..
298 " takes stuff from chest at "..minetest
.pos_to_string(pos
))
302 function default
.furnace_active(pos
, percent
, item_percent
)
308 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is active and constantly burning its fuel.")).."]"..
309 "label[2.75,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
310 "list[current_name;src;2.75,0.5;1,1;]"..
311 "label[2.75,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
312 "list[current_name;fuel;2.75,2.9;1,1;]"..
313 "label[2.75,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
314 "image[2.75,1.7;1,1;default_furnace_fire_bg.png^[lowpart:"..
315 (100-percent
)..":default_furnace_fire_fg.png]"..
316 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
317 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[lowpart:"..
318 (item_percent
*100)..":gui_furnace_arrow_fg.png^[transformR270]"..
319 "label[4.75,0.76;"..minetest
.formspec_escape(S("Output slots:")).."]"..
320 "list[current_name;dst;4.75,1.16;2,2;]"..
321 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
322 "list[current_player;main;0,4.25;8,1;]"..
323 "list[current_player;main;0,5.5;8,3;8]"..
324 "label[0,8.4;"..default
.gui_controls
.."]"..
325 default
.get_hotbar_bg(0,4.25)
329 function default
.get_furnace_active_formspec(pos
, percent
)
330 local meta
= minetest
.get_meta(pos
)local inv
= meta
:get_inventory()
331 local srclist
= inv
:get_list("src")
333 local aftercooked
= nil
335 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
337 local item_percent
= 0
339 item_percent
= meta
:get_float("src_time")/cooked
.time
342 return default
.furnace_active(pos
, percent
, item_percent
)
345 default
.furnace_inactive_formspec
=
350 "label[-0.1,-0.3;"..minetest
.formspec_escape(S("This furnace is inactive. Read the instructions to learn how to activate it.")).."]"..
351 "label[2.75,0.1;"..minetest
.formspec_escape(S("Source:")).."]"..
352 "list[current_name;src;2.75,0.5;1,1;]"..
353 "label[2.75,2.5;"..minetest
.formspec_escape(S("Fuel:")).."]"..
354 "list[current_name;fuel;2.75,2.9;1,1;]"..
355 "label[2.75,1.3;"..minetest
.formspec_escape(S("Flame:")).."]"..
356 "image[2.75,1.7;1,1;default_furnace_fire_bg.png]"..
357 "label[3.75,1.3;"..minetest
.formspec_escape(S("Progress:")).."]"..
358 "image[3.75,1.7;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
359 "label[4.75,0.76;"..minetest
.formspec_escape(S("Output slots:")).."]"..
360 "list[current_name;dst;4.75,1.16;2,2;]"..
361 "label[0,3.75;"..minetest
.formspec_escape(S("Player inventory:")).."]"..
362 "list[current_player;main;0,4.25;8,1;]"..
363 "list[current_player;main;0,5.5;8,3;8]"..
364 "label[0,8.4;"..default
.gui_controls
.."]"..
365 default
.get_hotbar_bg(0,4.25)
367 minetest
.register_node("default:furnace", {
368 description
= S("furnace"),
369 tiles
= {"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_side.png",
370 "default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
371 paramtype2
= "facedir",
372 groups
= {immortal
=1},
373 legacy_facedir_simple
= true,
374 is_ground_content
= false,
375 sounds
= default
.node_sound_stone_defaults(),
376 on_construct
= function(pos
)
377 local meta
= minetest
.get_meta(pos
)
378 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
379 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"))
380 local inv
= meta
:get_inventory()
381 inv
:set_size("fuel", 1)
382 inv
:set_size("src", 1)
383 inv
:set_size("dst", 4)
385 can_dig
= function(pos
,player
)
386 local meta
= minetest
.get_meta(pos
);
387 local inv
= meta
:get_inventory()
388 if not inv
:is_empty("fuel") then
390 elseif not inv
:is_empty("dst") then
392 elseif not inv
:is_empty("src") then
397 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
398 if minetest
.is_protected(pos
, player
:get_player_name()) then
401 local meta
= minetest
.get_meta(pos
)
402 local inv
= meta
:get_inventory()
403 if listname
== "fuel" then
404 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
405 if inv
:is_empty("src") then
406 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
408 return stack
:get_count()
412 elseif listname
== "src" then
413 return stack
:get_count()
414 elseif listname
== "dst" then
418 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
419 if minetest
.is_protected(pos
, player
:get_player_name()) then
422 local meta
= minetest
.get_meta(pos
)
423 local inv
= meta
:get_inventory()
424 local stack
= inv
:get_stack(from_list
, from_index
)
425 if to_list
== "fuel" then
426 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
427 if inv
:is_empty("src") then
428 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
434 elseif to_list
== "src" then
436 elseif to_list
== "dst" then
440 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
441 if minetest
.is_protected(pos
, player
:get_player_name()) then
444 return stack
:get_count()
448 minetest
.register_node("default:furnace_active", {
449 description
= S("furnace"),
451 "default_furnace_top.png",
452 "default_furnace_bottom.png",
453 "default_furnace_side.png",
454 "default_furnace_side.png",
455 "default_furnace_side.png",
457 image
= "default_furnace_front_active.png",
458 backface_culling
= false,
460 type = "vertical_frames",
467 paramtype2
= "facedir",
469 drop
= "default:furnace",
470 groups
= {immortal
=1,not_in_creative_inventory
=1,hot
=1},
471 legacy_facedir_simple
= true,
472 is_ground_content
= false,
473 sounds
= default
.node_sound_stone_defaults(),
474 on_construct
= function(pos
)
475 local meta
= minetest
.get_meta(pos
)
476 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
477 meta
:set_string("infotext", S("Inactive furnace (Rightclick to examine)"));
478 local inv
= meta
:get_inventory()
479 inv
:set_size("fuel", 1)
480 inv
:set_size("src", 1)
481 inv
:set_size("dst", 4)
483 can_dig
= function(pos
,player
)
484 local meta
= minetest
.get_meta(pos
);
485 local inv
= meta
:get_inventory()
486 if not inv
:is_empty("fuel") then
488 elseif not inv
:is_empty("dst") then
490 elseif not inv
:is_empty("src") then
495 allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
496 if minetest
.is_protected(pos
, player
:get_player_name()) then
499 local meta
= minetest
.get_meta(pos
)
500 local inv
= meta
:get_inventory()
501 if listname
== "fuel" then
502 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
503 if inv
:is_empty("src") then
504 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
506 return stack
:get_count()
510 elseif listname
== "src" then
511 return stack
:get_count()
512 elseif listname
== "dst" then
516 allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
517 if minetest
.is_protected(pos
, player
:get_player_name()) then
520 local meta
= minetest
.get_meta(pos
)
521 local inv
= meta
:get_inventory()
522 local stack
= inv
:get_stack(from_list
, from_index
)
523 if to_list
== "fuel" then
524 if minetest
.get_craft_result({method
="fuel",width
=1,items
={stack
}}).time
~= 0 then
525 if inv
:is_empty("src") then
526 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
532 elseif to_list
== "src" then
534 elseif to_list
== "dst" then
538 allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
539 if minetest
.is_protected(pos
, player
:get_player_name()) then
542 return stack
:get_count()
546 local function swap_node(pos
,name
)
547 local node
= minetest
.get_node(pos
)
548 if node
.name
== name
then
552 minetest
.swap_node(pos
,node
)
555 minetest
.register_abm({
556 nodenames
= {"default:chest"},
559 action
= function(pos
,node
,active_object_count
, active_object_count_wider
)
560 local meta
= minetest
.get_meta(pos
)
561 meta
:set_string("formspec", default
.chest_formspec())
562 meta
:set_string("infotext", S("Chest (Rightclick to open)"))
566 minetest
.register_abm({
567 nodenames
= {"default:furnace","default:furnace_active"},
570 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
571 local meta
= minetest
.get_meta(pos
)
572 for i
, name
in ipairs({
578 if meta
:get_string(name
) == "" then
579 meta
:set_float(name
, 0.0)
583 local inv
= meta
:get_inventory()
585 local srclist
= inv
:get_list("src")
590 cooked
, aftercooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
593 local was_active
= false
595 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
597 meta
:set_float("fuel_time", meta
:get_float("fuel_time") + 1)
598 meta
:set_float("src_time", meta
:get_float("src_time") + 1)
599 if cooked
and cooked
.item
and meta
:get_float("src_time") >= cooked
.time
then
600 -- check if there's room for output in "dst" list
601 if inv
:room_for_item("dst",cooked
.item
) then
602 -- Put result in "dst" list
603 inv
:add_item("dst", cooked
.item
)
604 -- take stuff from "src" list
605 inv
:set_stack("src", 1, aftercooked
.items
[1])
607 --print("Could not insert '"..cooked.item:to_string().."'")
609 meta
:set_string("src_time", 0)
613 if meta
:get_float("fuel_time") < meta
:get_float("fuel_totaltime") then
614 local percent
= math
.floor(meta
:get_float("fuel_time") /
615 meta
:get_float("fuel_totaltime") * 100)
616 meta
:set_string("infotext",string.format(S("Active furnace (Flame used: %d%%) (Rightclick to examine)"), percent
))
617 swap_node(pos
,"default:furnace_active")
618 meta
:set_string("formspec",default
.get_furnace_active_formspec(pos
, percent
))
625 local fuellist
= inv
:get_list("fuel")
626 local srclist
= inv
:get_list("src")
629 cooked
= minetest
.get_craft_result({method
= "cooking", width
= 1, items
= srclist
})
632 fuel
, afterfuel
= minetest
.get_craft_result({method
= "fuel", width
= 1, items
= fuellist
})
635 if not fuel
or fuel
.time
<= 0 then
636 meta
:set_string("infotext",S("Furnace without fuel (Rightclick to examine)"))
637 swap_node(pos
,"default:furnace")
638 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
642 if cooked
.item
:is_empty() then
644 meta
:set_string("infotext",S("Empty furnace (Rightclick to examine)"))
645 swap_node(pos
,"default:furnace")
646 meta
:set_string("formspec", default
.furnace_inactive_formspec
)
651 meta
:set_string("fuel_totaltime", fuel
.time
)
652 meta
:set_string("fuel_time", 0)
654 inv
:set_stack("fuel", 1, afterfuel
.items
[1])
658 minetest
.register_node("default:cobble", {
659 description
= S("cobblestone"),
660 tiles
= {"default_cobble.png"},
661 is_ground_content
= true,
662 groups
= {cracky
=3, stone
=2},
663 sounds
= default
.node_sound_stone_defaults(),
666 minetest
.register_node("default:apple", {
667 description
= S("apple"),
668 drawtype
= "plantlike",
670 tiles
= {"default_apple.png"},
671 inventory_image
= "default_apple.png",
673 sunlight_propagates
= true,
675 is_ground_content
= true,
678 fixed
= {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
680 groups
= {fleshy
=3,dig_immediate
=3,flammable
=2,leafdecay
=3,leafdecay_drop
=1},
681 on_use
= minetest
.item_eat(1),
682 sounds
= default
.node_sound_leaves_defaults(),
683 after_place_node
= function(pos
, placer
, itemstack
)
684 if placer
:is_player() then
685 minetest
.set_node(pos
, {name
="default:apple", param2
=1})