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)
49 -- Node will be called stairs:stair_<subname>
51 function stairs.register_stair(subname, recipeitem, groups, images, description,
52 sounds, worldaligntex)
53 -- Set backface culling and world-aligned textures
54 local stair_images = {}
55 for i, image in ipairs(images) do
56 if type(image) == "string" then
59 backface_culling = true,
62 stair_images[i].align_style = "world"
65 stair_images[i] = table.copy(image)
66 if stair_images[i].backface_culling == nil then
67 stair_images[i].backface_culling = true
69 if worldaligntex and stair_images[i].align_style == nil then
70 stair_images[i].align_style = "world"
74 local new_groups = table.copy(groups)
76 minetest.register_node(":stairs:stair_" .. subname, {
77 description = description,
81 paramtype2 = "facedir",
82 is_ground_content = false,
88 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
89 {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5},
92 on_place = function(itemstack, placer, pointed_thing)
93 if pointed_thing.type ~= "node" then
97 return rotate_and_place(itemstack, placer, pointed_thing)
103 minetest.register_node(":stairs:stair_" .. subname .. "upside_down", {
104 replace_name = "stairs:stair_" .. subname,
105 groups = {slabs_replace = 1},
110 -- Recipe matches appearence in inventory
111 minetest.register_craft({
112 output = 'stairs:stair_' .. subname .. ' 8',
114 {"", "", recipeitem},
115 {"", recipeitem, recipeitem},
116 {recipeitem, recipeitem, recipeitem},
120 -- Use stairs to craft full blocks again (1:1)
121 minetest.register_craft({
122 output = recipeitem .. ' 3',
124 {'stairs:stair_' .. subname, 'stairs:stair_' .. subname},
125 {'stairs:stair_' .. subname, 'stairs:stair_' .. subname},
130 local baseburntime = minetest.get_craft_result({
135 if baseburntime > 0 then
136 minetest.register_craft({
138 recipe = 'stairs:stair_' .. subname,
139 burntime = math.floor(baseburntime * 0.75),
147 -- Node will be called stairs:slab_<subname>
149 function stairs.register_slab(subname, recipeitem, groups, images, description,
150 sounds, worldaligntex)
151 -- Set world-aligned textures
152 local slab_images = {}
153 for i, image in ipairs(images) do
154 if type(image) == "string" then
158 if worldaligntex then
159 slab_images[i].align_style = "world"
162 slab_images[i] = table.copy(image)
163 if worldaligntex and image.align_style == nil then
164 slab_images[i].align_style = "world"
168 local new_groups = table.copy(groups)
170 minetest.register_node(":stairs:slab_" .. subname, {
171 description = description,
172 drawtype = "nodebox",
175 paramtype2 = "facedir",
176 is_ground_content = false,
181 fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
183 on_place = function(itemstack, placer, pointed_thing)
184 local under = minetest.get_node(pointed_thing.under)
185 local wield_item = itemstack:get_name()
186 local player_name = placer and placer:get_player_name() or ""
187 local creative_enabled = (creative and creative.is_enabled_for
188 and creative.is_enabled_for(player_name))
190 if under and under.name:find("^stairs:slab_") then
191 -- place slab using under node orientation
192 local dir = minetest.dir_to_facedir(vector.subtract(
193 pointed_thing.above, pointed_thing.under), true)
195 local p2 = under.param2
197 -- Placing a slab on an upside down slab should make it right-side up.
198 if p2 >= 20 and dir == 8 then
200 -- same for the opposite case: slab below normal slab
201 elseif p2 <= 3 and dir == 4 then
205 -- else attempt to place node with proper param2
206 minetest.item_place_node(ItemStack(wield_item), placer, pointed_thing, p2)
207 if not creative_enabled then
208 itemstack:take_item()
212 return rotate_and_place(itemstack, placer, pointed_thing)
219 minetest.register_node(":stairs:slab_" .. subname .. "upside_down", {
220 replace_name = "stairs:slab_".. subname,
221 groups = {slabs_replace = 1},
226 minetest.register_craft({
227 output = 'stairs:slab_' .. subname .. ' 6',
229 {recipeitem, recipeitem, recipeitem},
233 -- Use 2 slabs to craft a full block again (1:1)
234 minetest.register_craft({
237 {'stairs:slab_' .. subname},
238 {'stairs:slab_' .. subname},
243 local baseburntime = minetest.get_craft_result({
248 if baseburntime > 0 then
249 minetest.register_craft({
251 recipe = 'stairs:slab_' .. subname,
252 burntime = math.floor(baseburntime * 0.5),
259 -- Optionally replace old "upside_down" nodes with new param2 versions.
260 -- Disabled by default.
263 minetest.register_abm({
264 label = "Slab replace",
265 nodenames = {"group:slabs_replace"},
268 action = function(pos, node)
269 node.name = minetest.registered_nodes[node.name].replace_name
270 node.param2 = node.param2 + 20
271 if node.param2 == 21 then
273 elseif node.param2 == 23 then
276 minetest.set_node(pos, node)
282 -- Register inner stair
283 -- Node will be called stairs:stair_inner_<subname>
285 function stairs.register_stair_inner(subname, recipeitem, groups, images,
286 description, sounds, worldaligntex)
287 -- Set backface culling and world-aligned textures
288 local stair_images = {}
289 for i, image in ipairs(images) do
290 if type(image) == "string" then
293 backface_culling = true,
295 if worldaligntex then
296 stair_images[i].align_style = "world"
299 stair_images[i] = table.copy(image)
300 if stair_images[i].backface_culling == nil then
301 stair_images[i].backface_culling = true
303 if worldaligntex and stair_images[i].align_style == nil then
304 stair_images[i].align_style = "world"
308 local new_groups = table.copy(groups)
310 minetest.register_node(":stairs:stair_inner_" .. subname, {
311 description = "Inner " .. description,
312 drawtype = "nodebox",
313 tiles = stair_images,
315 paramtype2 = "facedir",
316 is_ground_content = false,
322 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
323 {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5},
324 {-0.5, 0.0, -0.5, 0.0, 0.5, 0.0},
327 on_place = function(itemstack, placer, pointed_thing)
328 if pointed_thing.type ~= "node" then
332 return rotate_and_place(itemstack, placer, pointed_thing)
337 minetest.register_craft({
338 output = 'stairs:stair_inner_' .. subname .. ' 7',
340 { "", recipeitem, ""},
341 { recipeitem, "", recipeitem},
342 {recipeitem, recipeitem, recipeitem},
347 local baseburntime = minetest.get_craft_result({
352 if baseburntime > 0 then
353 minetest.register_craft({
355 recipe = 'stairs:stair_inner_' .. subname,
356 burntime = math.floor(baseburntime * 0.875),
363 -- Register outer stair
364 -- Node will be called stairs:stair_outer_<subname>
366 function stairs.register_stair_outer(subname, recipeitem, groups, images,
367 description, sounds, worldaligntex)
368 -- Set backface culling and world-aligned textures
369 local stair_images = {}
370 for i, image in ipairs(images) do
371 if type(image) == "string" then
374 backface_culling = true,
376 if worldaligntex then
377 stair_images[i].align_style = "world"
380 stair_images[i] = table.copy(image)
381 if stair_images[i].backface_culling == nil then
382 stair_images[i].backface_culling = true
384 if worldaligntex and stair_images[i].align_style == nil then
385 stair_images[i].align_style = "world"
389 local new_groups = table.copy(groups)
391 minetest.register_node(":stairs:stair_outer_" .. subname, {
392 description = "Outer " .. description,
393 drawtype = "nodebox",
394 tiles = stair_images,
396 paramtype2 = "facedir",
397 is_ground_content = false,
403 {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5},
404 {-0.5, 0.0, 0.0, 0.0, 0.5, 0.5},
407 on_place = function(itemstack, placer, pointed_thing)
408 if pointed_thing.type ~= "node" then
412 return rotate_and_place(itemstack, placer, pointed_thing)
417 minetest.register_craft({
418 output = 'stairs:stair_outer_' .. subname .. ' 6',
421 { "", recipeitem, ""},
422 {recipeitem, recipeitem, recipeitem},
427 local baseburntime = minetest.get_craft_result({
432 if baseburntime > 0 then
433 minetest.register_craft({
435 recipe = 'stairs:stair_outer_' .. subname,
436 burntime = math.floor(baseburntime * 0.625),
443 -- Stair/slab registration function.
444 -- Nodes will be called stairs:{stair,slab}_<subname>
446 function stairs.register_stair_and_slab(subname, recipeitem, groups, images,
447 desc_stair, desc_slab, sounds, worldaligntex)
448 stairs.register_stair(subname, recipeitem, groups, images, desc_stair,
449 sounds, worldaligntex)
450 stairs.register_stair_inner(subname, recipeitem, groups, images, desc_stair,
451 sounds, worldaligntex)
452 stairs.register_stair_outer(subname, recipeitem, groups, images, desc_stair,
453 sounds, worldaligntex)
454 stairs.register_slab(subname, recipeitem, groups, images, desc_slab,
455 sounds, worldaligntex)
459 -- Register default stairs and slabs
461 stairs.register_stair_and_slab(
464 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
465 {"default_wood.png"},
468 default.node_sound_wood_defaults(),
472 stairs.register_stair_and_slab(
474 "default:junglewood",
475 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
476 {"default_junglewood.png"},
479 default.node_sound_wood_defaults(),
483 stairs.register_stair_and_slab(
486 {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
487 {"default_pine_wood.png"},
490 default.node_sound_wood_defaults(),
494 stairs.register_stair_and_slab(
496 "default:acacia_wood",
497 {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
498 {"default_acacia_wood.png"},
501 default.node_sound_wood_defaults(),
505 stairs.register_stair_and_slab(
507 "default:aspen_wood",
508 {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
509 {"default_aspen_wood.png"},
512 default.node_sound_wood_defaults(),
516 stairs.register_stair_and_slab(
520 {"default_stone.png"},
523 default.node_sound_stone_defaults(),
527 stairs.register_stair_and_slab(
531 {"default_cobble.png"},
534 default.node_sound_stone_defaults(),
538 stairs.register_stair_and_slab(
540 "default:mossycobble",
542 {"default_mossycobble.png"},
543 "Mossy Cobblestone Stair",
544 "Mossy Cobblestone Slab",
545 default.node_sound_stone_defaults(),
549 stairs.register_stair_and_slab(
551 "default:stonebrick",
553 {"default_stone_brick.png"},
556 default.node_sound_stone_defaults(),
560 stairs.register_stair_and_slab(
562 "default:stone_block",
564 {"default_stone_block.png"},
567 default.node_sound_stone_defaults(),
571 stairs.register_stair_and_slab(
573 "default:desert_stone",
575 {"default_desert_stone.png"},
576 "Desert Stone Stair",
578 default.node_sound_stone_defaults(),
582 stairs.register_stair_and_slab(
584 "default:desert_cobble",
586 {"default_desert_cobble.png"},
587 "Desert Cobblestone Stair",
588 "Desert Cobblestone Slab",
589 default.node_sound_stone_defaults(),
593 stairs.register_stair_and_slab(
595 "default:desert_stonebrick",
597 {"default_desert_stone_brick.png"},
598 "Desert Stone Brick Stair",
599 "Desert Stone Brick Slab",
600 default.node_sound_stone_defaults(),
604 stairs.register_stair_and_slab(
605 "desert_stone_block",
606 "default:desert_stone_block",
608 {"default_desert_stone_block.png"},
609 "Desert Stone Block Stair",
610 "Desert Stone Block Slab",
611 default.node_sound_stone_defaults(),
615 stairs.register_stair_and_slab(
618 {crumbly = 1, cracky = 3},
619 {"default_sandstone.png"},
622 default.node_sound_stone_defaults(),
626 stairs.register_stair_and_slab(
628 "default:sandstonebrick",
630 {"default_sandstone_brick.png"},
631 "Sandstone Brick Stair",
632 "Sandstone Brick Slab",
633 default.node_sound_stone_defaults(),
637 stairs.register_stair_and_slab(
639 "default:sandstone_block",
641 {"default_sandstone_block.png"},
642 "Sandstone Block Stair",
643 "Sandstone Block Slab",
644 default.node_sound_stone_defaults(),
648 stairs.register_stair_and_slab(
650 "default:desert_sandstone",
651 {crumbly = 1, cracky = 3},
652 {"default_desert_sandstone.png"},
653 "Desert Sandstone Stair",
654 "Desert Sandstone Slab",
655 default.node_sound_stone_defaults(),
659 stairs.register_stair_and_slab(
660 "desert_sandstone_brick",
661 "default:desert_sandstone_brick",
663 {"default_desert_sandstone_brick.png"},
664 "Desert Sandstone Brick Stair",
665 "Desert Sandstone Brick Slab",
666 default.node_sound_stone_defaults(),
670 stairs.register_stair_and_slab(
671 "desert_sandstone_block",
672 "default:desert_sandstone_block",
674 {"default_desert_sandstone_block.png"},
675 "Desert Sandstone Block Stair",
676 "Desert Sandstone Block Slab",
677 default.node_sound_stone_defaults(),
681 stairs.register_stair_and_slab(
683 "default:silver_sandstone",
684 {crumbly = 1, cracky = 3},
685 {"default_silver_sandstone.png"},
686 "Silver Sandstone Stair",
687 "Silver Sandstone Slab",
688 default.node_sound_stone_defaults(),
692 stairs.register_stair_and_slab(
693 "silver_sandstone_brick",
694 "default:silver_sandstone_brick",
696 {"default_silver_sandstone_brick.png"},
697 "Silver Sandstone Brick Stair",
698 "Silver Sandstone Brick Slab",
699 default.node_sound_stone_defaults(),
703 stairs.register_stair_and_slab(
704 "silver_sandstone_block",
705 "default:silver_sandstone_block",
707 {"default_silver_sandstone_block.png"},
708 "Silver Sandstone Block Stair",
709 "Silver Sandstone Block Slab",
710 default.node_sound_stone_defaults(),
714 stairs.register_stair_and_slab(
717 {cracky = 1, level = 2},
718 {"default_obsidian.png"},
721 default.node_sound_stone_defaults(),
725 stairs.register_stair_and_slab(
727 "default:obsidianbrick",
728 {cracky = 1, level = 2},
729 {"default_obsidian_brick.png"},
730 "Obsidian Brick Stair",
731 "Obsidian Brick Slab",
732 default.node_sound_stone_defaults(),
736 stairs.register_stair_and_slab(
738 "default:obsidian_block",
739 {cracky = 1, level = 2},
740 {"default_obsidian_block.png"},
741 "Obsidian Block Stair",
742 "Obsidian Block Slab",
743 default.node_sound_stone_defaults(),
747 stairs.register_stair_and_slab(
751 {"default_brick.png"},
754 default.node_sound_stone_defaults(),
758 stairs.register_stair_and_slab(
760 "default:steelblock",
761 {cracky = 1, level = 2},
762 {"default_steel_block.png"},
765 default.node_sound_metal_defaults(),
769 stairs.register_stair_and_slab(
772 {cracky = 1, level = 2},
773 {"default_tin_block.png"},
776 default.node_sound_metal_defaults(),
780 stairs.register_stair_and_slab(
782 "default:copperblock",
783 {cracky = 1, level = 2},
784 {"default_copper_block.png"},
785 "Copper Block Stair",
787 default.node_sound_metal_defaults(),
791 stairs.register_stair_and_slab(
793 "default:bronzeblock",
794 {cracky = 1, level = 2},
795 {"default_bronze_block.png"},
796 "Bronze Block Stair",
798 default.node_sound_metal_defaults(),
802 stairs.register_stair_and_slab(
806 {"default_gold_block.png"},
809 default.node_sound_metal_defaults(),
813 stairs.register_stair_and_slab(
816 {cracky = 3, cools_lava = 1, slippery = 3},
820 default.node_sound_glass_defaults(),
824 stairs.register_stair_and_slab(
827 {crumbly = 3, cools_lava = 1, snowy = 1},
828 {"default_snow.png"},
831 default.node_sound_snow_defaults(),
835 -- Glass stair nodes need to be registered individually to utilize specialized textures.
837 stairs.register_stair(
841 {"stairs_glass_split.png", "default_glass.png",
842 "stairs_glass_stairside.png^[transformFX", "stairs_glass_stairside.png",
843 "default_glass.png", "stairs_glass_split.png"},
845 default.node_sound_glass_defaults(),
849 stairs.register_slab(
853 {"default_glass.png", "default_glass.png", "stairs_glass_split.png"},
855 default.node_sound_glass_defaults(),
859 stairs.register_stair_inner(
863 {"stairs_glass_stairside.png^[transformR270", "default_glass.png",
864 "stairs_glass_stairside.png^[transformFX", "default_glass.png",
865 "default_glass.png", "stairs_glass_stairside.png"},
867 default.node_sound_glass_defaults(),
871 stairs.register_stair_outer(
875 {"stairs_glass_stairside.png^[transformR90", "default_glass.png",
876 "stairs_glass_outer_stairside.png", "stairs_glass_stairside.png",
877 "stairs_glass_stairside.png^[transformR90","stairs_glass_outer_stairside.png"},
879 default.node_sound_glass_defaults(),
883 stairs.register_stair(
885 "default:obsidian_glass",
887 {"stairs_obsidian_glass_split.png", "default_obsidian_glass.png",
888 "stairs_obsidian_glass_stairside.png^[transformFX", "stairs_obsidian_glass_stairside.png",
889 "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"},
890 "Obsidian Glass Stair",
891 default.node_sound_glass_defaults(),
895 stairs.register_slab(
897 "default:obsidian_glass",
899 {"default_obsidian_glass.png", "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"},
900 "Obsidian Glass Slab",
901 default.node_sound_glass_defaults(),
905 stairs.register_stair_inner(
907 "default:obsidian_glass",
909 {"stairs_obsidian_glass_stairside.png^[transformR270", "default_obsidian_glass.png",
910 "stairs_obsidian_glass_stairside.png^[transformFX", "default_obsidian_glass.png",
911 "default_obsidian_glass.png", "stairs_obsidian_glass_stairside.png"},
912 "Obsidian Glass Stair",
913 default.node_sound_glass_defaults(),
917 stairs.register_stair_outer(
919 "default:obsidian_glass",
921 {"stairs_obsidian_glass_stairside.png^[transformR90", "default_obsidian_glass.png",
922 "stairs_obsidian_glass_outer_stairside.png", "stairs_obsidian_glass_stairside.png",
923 "stairs_obsidian_glass_stairside.png^[transformR90","stairs_obsidian_glass_outer_stairside.png"},
924 "Obsidian Glass Stair",
925 default.node_sound_glass_defaults(),