1 -- Minetest 0.4 mod: stairs
2 -- See README.txt for licensing and other information.
5 -- Global namespace for functions
10 -- Register aliases for new pine node names
12 minetest.register_alias("stairs:stair_pinewood", "stairs:stair_pine_wood")
13 minetest.register_alias("stairs:slab_pinewood", "stairs:slab_pine_wood")
16 -- Get setting for replace ABM
18 local replace = minetest.settings:get_bool("enable_stairs_replace_abm")
20 local function rotate_and_place(itemstack, placer, pointed_thing)
21 local p0 = pointed_thing.under
22 local p1 = pointed_thing.above
26 local placer_pos = placer:get_pos()
28 param2 = minetest.dir_to_facedir(vector.subtract(p1, placer_pos))
31 local finepos = minetest.pointed_thing_to_face_pos(placer, pointed_thing)
32 local fpos = finepos.y % 1
34 if p0.y - 1 == p1.y or (fpos > 0 and fpos < 0.5)
35 or (fpos < -0.5 and fpos > -0.999999999) then
39 elseif param2 == 23 then
44 return minetest.item_place(itemstack, placer, pointed_thing, param2)
47 local function warn_if_exists(nodename)
48 if minetest.registered_nodes[nodename] then
49 minetest.log("warning", "Overwriting stairs node: " .. nodename)
55 -- Node will be called stairs:stair_<subname>
57 function stairs.register_stair(subname, recipeitem, groups, images, description,
58 sounds, worldaligntex)
59 -- Set backface culling and world-aligned textures
60 local stair_images = {}
61 for i, image in ipairs(images) do
62 if type(image) == "string" then
65 backface_culling = true,
68 stair_images[i].align_style = "world"
71 stair_images[i] = table.copy(image)
72 if stair_images[i].backface_culling == nil then
73 stair_images[i].backface_culling = true
75 if worldaligntex and stair_images[i].align_style == nil then
76 stair_images[i].align_style = "world"
80 local new_groups = table.copy(groups)
82 warn_if_exists("stairs:stair_" .. subname)
83 minetest.register_node(":stairs:stair_" .. subname, {
84 description = description,
88 paramtype2 = "facedir",
89 is_ground_content = false,
95 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
96 {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5},
99 on_place = function(itemstack, placer, pointed_thing)
100 if pointed_thing.type ~= "node" then
104 return rotate_and_place(itemstack, placer, pointed_thing)
110 minetest.register_node(":stairs:stair_" .. subname .. "upside_down", {
111 replace_name = "stairs:stair_" .. subname,
112 groups = {slabs_replace = 1},
117 -- Recipe matches appearence in inventory
118 minetest.register_craft({
119 output = "stairs:stair_" .. subname .. " 8",
121 {"", "", recipeitem},
122 {"", recipeitem, recipeitem},
123 {recipeitem, recipeitem, recipeitem},
127 -- Use stairs to craft full blocks again (1:1)
128 minetest.register_craft({
129 output = recipeitem .. " 3",
131 {"stairs:stair_" .. subname, "stairs:stair_" .. subname},
132 {"stairs:stair_" .. subname, "stairs:stair_" .. subname},
137 local baseburntime = minetest.get_craft_result({
142 if baseburntime > 0 then
143 minetest.register_craft({
145 recipe = "stairs:stair_" .. subname,
146 burntime = math.floor(baseburntime * 0.75),
154 -- Node will be called stairs:slab_<subname>
156 function stairs.register_slab(subname, recipeitem, groups, images, description,
157 sounds, worldaligntex)
158 -- Set world-aligned textures
159 local slab_images = {}
160 for i, image in ipairs(images) do
161 if type(image) == "string" then
165 if worldaligntex then
166 slab_images[i].align_style = "world"
169 slab_images[i] = table.copy(image)
170 if worldaligntex and image.align_style == nil then
171 slab_images[i].align_style = "world"
175 local new_groups = table.copy(groups)
177 warn_if_exists("stairs:slab_" .. subname)
178 minetest.register_node(":stairs:slab_" .. subname, {
179 description = description,
180 drawtype = "nodebox",
183 paramtype2 = "facedir",
184 is_ground_content = false,
189 fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
191 on_place = function(itemstack, placer, pointed_thing)
192 local under = minetest.get_node(pointed_thing.under)
193 local wield_item = itemstack:get_name()
194 local player_name = placer and placer:get_player_name() or ""
195 local creative_enabled = (creative and creative.is_enabled_for
196 and creative.is_enabled_for(player_name))
198 if under and under.name:find("^stairs:slab_") then
199 -- place slab using under node orientation
200 local dir = minetest.dir_to_facedir(vector.subtract(
201 pointed_thing.above, pointed_thing.under), true)
203 local p2 = under.param2
205 -- Placing a slab on an upside down slab should make it right-side up.
206 if p2 >= 20 and dir == 8 then
208 -- same for the opposite case: slab below normal slab
209 elseif p2 <= 3 and dir == 4 then
213 -- else attempt to place node with proper param2
214 minetest.item_place_node(ItemStack(wield_item), placer, pointed_thing, p2)
215 if not creative_enabled then
216 itemstack:take_item()
220 return rotate_and_place(itemstack, placer, pointed_thing)
227 minetest.register_node(":stairs:slab_" .. subname .. "upside_down", {
228 replace_name = "stairs:slab_".. subname,
229 groups = {slabs_replace = 1},
234 minetest.register_craft({
235 output = "stairs:slab_" .. subname .. " 6",
237 {recipeitem, recipeitem, recipeitem},
241 -- Use 2 slabs to craft a full block again (1:1)
242 minetest.register_craft({
245 {"stairs:slab_" .. subname},
246 {"stairs:slab_" .. subname},
251 local baseburntime = minetest.get_craft_result({
256 if baseburntime > 0 then
257 minetest.register_craft({
259 recipe = "stairs:slab_" .. subname,
260 burntime = math.floor(baseburntime * 0.5),
267 -- Optionally replace old "upside_down" nodes with new param2 versions.
268 -- Disabled by default.
271 minetest.register_abm({
272 label = "Slab replace",
273 nodenames = {"group:slabs_replace"},
276 action = function(pos, node)
277 node.name = minetest.registered_nodes[node.name].replace_name
278 node.param2 = node.param2 + 20
279 if node.param2 == 21 then
281 elseif node.param2 == 23 then
284 minetest.set_node(pos, node)
290 -- Register inner stair
291 -- Node will be called stairs:stair_inner_<subname>
293 function stairs.register_stair_inner(subname, recipeitem, groups, images,
294 description, sounds, worldaligntex)
295 -- Set backface culling and world-aligned textures
296 local stair_images = {}
297 for i, image in ipairs(images) do
298 if type(image) == "string" then
301 backface_culling = true,
303 if worldaligntex then
304 stair_images[i].align_style = "world"
307 stair_images[i] = table.copy(image)
308 if stair_images[i].backface_culling == nil then
309 stair_images[i].backface_culling = true
311 if worldaligntex and stair_images[i].align_style == nil then
312 stair_images[i].align_style = "world"
316 local new_groups = table.copy(groups)
318 warn_if_exists("stairs:stair_inner_" .. subname)
319 minetest.register_node(":stairs:stair_inner_" .. subname, {
320 description = "Inner " .. description,
321 drawtype = "nodebox",
322 tiles = stair_images,
324 paramtype2 = "facedir",
325 is_ground_content = false,
331 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
332 {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5},
333 {-0.5, 0.0, -0.5, 0.0, 0.5, 0.0},
336 on_place = function(itemstack, placer, pointed_thing)
337 if pointed_thing.type ~= "node" then
341 return rotate_and_place(itemstack, placer, pointed_thing)
346 minetest.register_craft({
347 output = "stairs:stair_inner_" .. subname .. " 7",
349 {"", recipeitem, ""},
350 {recipeitem, "", recipeitem},
351 {recipeitem, recipeitem, recipeitem},
356 local baseburntime = minetest.get_craft_result({
361 if baseburntime > 0 then
362 minetest.register_craft({
364 recipe = "stairs:stair_inner_" .. subname,
365 burntime = math.floor(baseburntime * 0.875),
372 -- Register outer stair
373 -- Node will be called stairs:stair_outer_<subname>
375 function stairs.register_stair_outer(subname, recipeitem, groups, images,
376 description, sounds, worldaligntex)
377 -- Set backface culling and world-aligned textures
378 local stair_images = {}
379 for i, image in ipairs(images) do
380 if type(image) == "string" then
383 backface_culling = true,
385 if worldaligntex then
386 stair_images[i].align_style = "world"
389 stair_images[i] = table.copy(image)
390 if stair_images[i].backface_culling == nil then
391 stair_images[i].backface_culling = true
393 if worldaligntex and stair_images[i].align_style == nil then
394 stair_images[i].align_style = "world"
398 local new_groups = table.copy(groups)
400 warn_if_exists("stairs:stair_outer_" .. subname)
401 minetest.register_node(":stairs:stair_outer_" .. subname, {
402 description = "Outer " .. description,
403 drawtype = "nodebox",
404 tiles = stair_images,
406 paramtype2 = "facedir",
407 is_ground_content = false,
413 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
414 {-0.5, 0.0, 0.0, 0.0, 0.5, 0.5},
417 on_place = function(itemstack, placer, pointed_thing)
418 if pointed_thing.type ~= "node" then
422 return rotate_and_place(itemstack, placer, pointed_thing)
427 minetest.register_craft({
428 output = "stairs:stair_outer_" .. subname .. " 6",
430 {"", recipeitem, ""},
431 {recipeitem, recipeitem, recipeitem},
436 local baseburntime = minetest.get_craft_result({
441 if baseburntime > 0 then
442 minetest.register_craft({
444 recipe = "stairs:stair_outer_" .. subname,
445 burntime = math.floor(baseburntime * 0.625),
452 -- Stair/slab registration function.
453 -- Nodes will be called stairs:{stair,slab}_<subname>
455 function stairs.register_stair_and_slab(subname, recipeitem, groups, images,
456 desc_stair, desc_slab, sounds, worldaligntex)
457 stairs.register_stair(subname, recipeitem, groups, images, desc_stair,
458 sounds, worldaligntex)
459 stairs.register_stair_inner(subname, recipeitem, groups, images, desc_stair,
460 sounds, worldaligntex)
461 stairs.register_stair_outer(subname, recipeitem, groups, images, desc_stair,
462 sounds, worldaligntex)
463 stairs.register_slab(subname, recipeitem, groups, images, desc_slab,
464 sounds, worldaligntex)
468 -- Register default stairs and slabs
470 stairs.register_stair_and_slab(
473 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
474 {"default_wood.png"},
477 default.node_sound_wood_defaults(),
481 stairs.register_stair_and_slab(
483 "default:junglewood",
484 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
485 {"default_junglewood.png"},
488 default.node_sound_wood_defaults(),
492 stairs.register_stair_and_slab(
495 {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
496 {"default_pine_wood.png"},
499 default.node_sound_wood_defaults(),
503 stairs.register_stair_and_slab(
505 "default:acacia_wood",
506 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
507 {"default_acacia_wood.png"},
510 default.node_sound_wood_defaults(),
514 stairs.register_stair_and_slab(
516 "default:aspen_wood",
517 {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
518 {"default_aspen_wood.png"},
521 default.node_sound_wood_defaults(),
525 stairs.register_stair_and_slab(
529 {"default_stone.png"},
532 default.node_sound_stone_defaults(),
536 stairs.register_stair_and_slab(
540 {"default_cobble.png"},
543 default.node_sound_stone_defaults(),
547 stairs.register_stair_and_slab(
549 "default:mossycobble",
551 {"default_mossycobble.png"},
552 "Mossy Cobblestone Stair",
553 "Mossy Cobblestone Slab",
554 default.node_sound_stone_defaults(),
558 stairs.register_stair_and_slab(
560 "default:stonebrick",
562 {"default_stone_brick.png"},
565 default.node_sound_stone_defaults(),
569 stairs.register_stair_and_slab(
571 "default:stone_block",
573 {"default_stone_block.png"},
576 default.node_sound_stone_defaults(),
580 stairs.register_stair_and_slab(
582 "default:desert_stone",
584 {"default_desert_stone.png"},
585 "Desert Stone Stair",
587 default.node_sound_stone_defaults(),
591 stairs.register_stair_and_slab(
593 "default:desert_cobble",
595 {"default_desert_cobble.png"},
596 "Desert Cobblestone Stair",
597 "Desert Cobblestone Slab",
598 default.node_sound_stone_defaults(),
602 stairs.register_stair_and_slab(
604 "default:desert_stonebrick",
606 {"default_desert_stone_brick.png"},
607 "Desert Stone Brick Stair",
608 "Desert Stone Brick Slab",
609 default.node_sound_stone_defaults(),
613 stairs.register_stair_and_slab(
614 "desert_stone_block",
615 "default:desert_stone_block",
617 {"default_desert_stone_block.png"},
618 "Desert Stone Block Stair",
619 "Desert Stone Block Slab",
620 default.node_sound_stone_defaults(),
624 stairs.register_stair_and_slab(
627 {crumbly = 1, cracky = 3},
628 {"default_sandstone.png"},
631 default.node_sound_stone_defaults(),
635 stairs.register_stair_and_slab(
637 "default:sandstonebrick",
639 {"default_sandstone_brick.png"},
640 "Sandstone Brick Stair",
641 "Sandstone Brick Slab",
642 default.node_sound_stone_defaults(),
646 stairs.register_stair_and_slab(
648 "default:sandstone_block",
650 {"default_sandstone_block.png"},
651 "Sandstone Block Stair",
652 "Sandstone Block Slab",
653 default.node_sound_stone_defaults(),
657 stairs.register_stair_and_slab(
659 "default:desert_sandstone",
660 {crumbly = 1, cracky = 3},
661 {"default_desert_sandstone.png"},
662 "Desert Sandstone Stair",
663 "Desert Sandstone Slab",
664 default.node_sound_stone_defaults(),
668 stairs.register_stair_and_slab(
669 "desert_sandstone_brick",
670 "default:desert_sandstone_brick",
672 {"default_desert_sandstone_brick.png"},
673 "Desert Sandstone Brick Stair",
674 "Desert Sandstone Brick Slab",
675 default.node_sound_stone_defaults(),
679 stairs.register_stair_and_slab(
680 "desert_sandstone_block",
681 "default:desert_sandstone_block",
683 {"default_desert_sandstone_block.png"},
684 "Desert Sandstone Block Stair",
685 "Desert Sandstone Block Slab",
686 default.node_sound_stone_defaults(),
690 stairs.register_stair_and_slab(
692 "default:silver_sandstone",
693 {crumbly = 1, cracky = 3},
694 {"default_silver_sandstone.png"},
695 "Silver Sandstone Stair",
696 "Silver Sandstone Slab",
697 default.node_sound_stone_defaults(),
701 stairs.register_stair_and_slab(
702 "silver_sandstone_brick",
703 "default:silver_sandstone_brick",
705 {"default_silver_sandstone_brick.png"},
706 "Silver Sandstone Brick Stair",
707 "Silver Sandstone Brick Slab",
708 default.node_sound_stone_defaults(),
712 stairs.register_stair_and_slab(
713 "silver_sandstone_block",
714 "default:silver_sandstone_block",
716 {"default_silver_sandstone_block.png"},
717 "Silver Sandstone Block Stair",
718 "Silver Sandstone Block Slab",
719 default.node_sound_stone_defaults(),
723 stairs.register_stair_and_slab(
726 {cracky = 1, level = 2},
727 {"default_obsidian.png"},
730 default.node_sound_stone_defaults(),
734 stairs.register_stair_and_slab(
736 "default:obsidianbrick",
737 {cracky = 1, level = 2},
738 {"default_obsidian_brick.png"},
739 "Obsidian Brick Stair",
740 "Obsidian Brick Slab",
741 default.node_sound_stone_defaults(),
745 stairs.register_stair_and_slab(
747 "default:obsidian_block",
748 {cracky = 1, level = 2},
749 {"default_obsidian_block.png"},
750 "Obsidian Block Stair",
751 "Obsidian Block Slab",
752 default.node_sound_stone_defaults(),
756 stairs.register_stair_and_slab(
760 {"default_brick.png"},
763 default.node_sound_stone_defaults(),
767 stairs.register_stair_and_slab(
769 "default:steelblock",
770 {cracky = 1, level = 2},
771 {"default_steel_block.png"},
774 default.node_sound_metal_defaults(),
778 stairs.register_stair_and_slab(
781 {cracky = 1, level = 2},
782 {"default_tin_block.png"},
785 default.node_sound_metal_defaults(),
789 stairs.register_stair_and_slab(
791 "default:copperblock",
792 {cracky = 1, level = 2},
793 {"default_copper_block.png"},
794 "Copper Block Stair",
796 default.node_sound_metal_defaults(),
800 stairs.register_stair_and_slab(
802 "default:bronzeblock",
803 {cracky = 1, level = 2},
804 {"default_bronze_block.png"},
805 "Bronze Block Stair",
807 default.node_sound_metal_defaults(),
811 stairs.register_stair_and_slab(
815 {"default_gold_block.png"},
818 default.node_sound_metal_defaults(),
822 stairs.register_stair_and_slab(
825 {cracky = 3, cools_lava = 1, slippery = 3},
829 default.node_sound_glass_defaults(),
833 stairs.register_stair_and_slab(
836 {crumbly = 3, cools_lava = 1, snowy = 1},
837 {"default_snow.png"},
840 default.node_sound_snow_defaults(),
844 -- Glass stair nodes need to be registered individually to utilize specialized textures.
846 stairs.register_stair(
850 {"stairs_glass_split.png", "default_glass.png",
851 "stairs_glass_stairside.png^[transformFX", "stairs_glass_stairside.png",
852 "default_glass.png", "stairs_glass_split.png"},
854 default.node_sound_glass_defaults(),
858 stairs.register_slab(
862 {"default_glass.png", "default_glass.png", "stairs_glass_split.png"},
864 default.node_sound_glass_defaults(),
868 stairs.register_stair_inner(
872 {"stairs_glass_stairside.png^[transformR270", "default_glass.png",
873 "stairs_glass_stairside.png^[transformFX", "default_glass.png",
874 "default_glass.png", "stairs_glass_stairside.png"},
876 default.node_sound_glass_defaults(),
880 stairs.register_stair_outer(
884 {"stairs_glass_stairside.png^[transformR90", "default_glass.png",
885 "stairs_glass_outer_stairside.png", "stairs_glass_stairside.png",
886 "stairs_glass_stairside.png^[transformR90","stairs_glass_outer_stairside.png"},
888 default.node_sound_glass_defaults(),
892 stairs.register_stair(
894 "default:obsidian_glass",
896 {"stairs_obsidian_glass_split.png", "default_obsidian_glass.png",
897 "stairs_obsidian_glass_stairside.png^[transformFX", "stairs_obsidian_glass_stairside.png",
898 "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"},
899 "Obsidian Glass Stair",
900 default.node_sound_glass_defaults(),
904 stairs.register_slab(
906 "default:obsidian_glass",
908 {"default_obsidian_glass.png", "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"},
909 "Obsidian Glass Slab",
910 default.node_sound_glass_defaults(),
914 stairs.register_stair_inner(
916 "default:obsidian_glass",
918 {"stairs_obsidian_glass_stairside.png^[transformR270", "default_obsidian_glass.png",
919 "stairs_obsidian_glass_stairside.png^[transformFX", "default_obsidian_glass.png",
920 "default_obsidian_glass.png", "stairs_obsidian_glass_stairside.png"},
921 "Obsidian Glass Stair",
922 default.node_sound_glass_defaults(),
926 stairs.register_stair_outer(
928 "default:obsidian_glass",
930 {"stairs_obsidian_glass_stairside.png^[transformR90", "default_obsidian_glass.png",
931 "stairs_obsidian_glass_outer_stairside.png", "stairs_obsidian_glass_stairside.png",
932 "stairs_obsidian_glass_stairside.png^[transformR90","stairs_obsidian_glass_outer_stairside.png"},
933 "Obsidian Glass Stair",
934 default.node_sound_glass_defaults(),