-- Minetest 0.4 mod: stairs -- See README.txt for licensing and other information. local modname = minetest.get_current_modname() local modpath = minetest.get_modpath (modname) local nodelight = ocutil.nodename_light_source -- Global namespace for functions stairs = {} stairs.snd_dirt = default.sound_dirt() stairs.snd_glass = default.sound_glass() stairs.snd_leaves = default.sound_leaves() stairs.snd_metal = default.sound_metal() stairs.snd_stone = default.sound_stone() stairs.snd_wood = default.sound_wood() -- Register aliases for new pine node names local nodepart local neednode local fromnode -- Get setting for replace ABM local replace = minetest.setting_getbool ("enable_stairs_replace_abm") local creative = minetest.setting_getbool ("creative_mode") -- =================================================================== function is_creative_enabled_for (name) if creative or minetest.check_player_privs (name, { creative = true }) then return true end return false end -- =================================================================== -- Placement helper local stair_place = function (itemstack, placer, pointed_thing, stair_node) -- If sneak is pressed, then use param2 in node pointed at when pla- -- cing. if placer:is_player() and placer:get_player_control().sneak then local name = placer:get_player_name() local pos_a = pointed_thing.above local node_a = minetest.get_node (pos_a) local def_a = minetest.registered_nodes [node_a.name] if not def_a or not def_a.buildable_to or minetest.is_protected (pos_a, name) then return itemstack end local pos_u = pointed_thing.under local node_u = minetest.get_node (pos_u) if minetest.get_item_group (node_u.name, "stair" ) > 0 or minetest.get_item_group (node_u.name, "slab" ) > 0 then minetest.set_node (pos_a, { name = stair_node, param2 = node_u.param2 }) if not is_creative_enabled_for (name) then itemstack:take_item() end return itemstack end end core.rotate_and_place (itemstack, placer, pointed_thing, is_creative_enabled_for (placer:get_player_name()), { invert_wall = placer:get_player_control().sneak }) return itemstack end -- =================================================================== -- Register stairs. -- Node will be called stairs:stair_ function stairs.register_stair (subname, recipeitem, groups, images, description, sounds) groups.stair = 1 minetest.register_node(":stairs:stair_" .. subname, { description = description, drawtype = "mesh", mesh = "stairs_stair.obj", tiles = images, paramtype = "light", paramtype2 = "facedir", light_source = nodelight (recipeitem) , is_ground_content = false, groups = groups, sounds = sounds, selection_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0.5, 0.5, 0.5} , } , } , collision_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0.5, 0.5, 0.5} , } , } , on_place = function (itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end local p0 = pointed_thing.under local p1 = pointed_thing.above local param2 = 0 local placer_pos = placer:getpos() if placer_pos then local dir = { x = p1.x - placer_pos.x, y = p1.y - placer_pos.y, z = p1.z - placer_pos.z } param2 = minetest.dir_to_facedir(dir) end if p0.y - 1 == p1.y then param2 = param2 + 20 if param2 == 21 then param2 = 23 elseif param2 == 23 then param2 = 21 end end return minetest.item_place(itemstack, placer, pointed_thing, param2) end, }) -- for replace ABM if replace then minetest.register_node(":stairs:stair_" .. subname .. "upside_down", { replace_name = "stairs:stair_" .. subname, groups = {slabs_replace = 1} , }) end minetest.register_craft({ output = 'stairs:stair_' .. subname .. ' 6', recipe = { {recipeitem, "", "" } , {recipeitem, recipeitem, "" } , {recipeitem, recipeitem, recipeitem} , } , }) -- Flipped recipe for the silly minecrafters minetest.register_craft({ output = 'stairs:stair_' .. subname .. ' 6', recipe = { {"", "", recipeitem} , {"", recipeitem, recipeitem} , {recipeitem, recipeitem, recipeitem} , } , }) end -- Register slabs. -- Node will be called stairs:slab_ function stairs.register_slab (subname, recipeitem, groups, images, description, sounds) groups.slab = 1 minetest.register_node(":stairs:slab_" .. subname, { description = description, drawtype = "nodebox", tiles = images, paramtype = "light", paramtype2 = "facedir", light_source = nodelight (recipeitem) , is_ground_content = false, groups = groups, sounds = sounds, node_box = { type = "fixed", fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , } , on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end -- If it's being placed on an another similar one, replace it with -- a full block local slabpos = nil local slabnode = nil local p0 = pointed_thing.under local p1 = pointed_thing.above local n0 = minetest.get_node(p0) local n1 = minetest.get_node(p1) local param2 = 0 local n0_is_upside_down = (n0.name == "stairs:slab_" .. subname and n0.param2 >= 20) if n0.name == "stairs:slab_" .. subname and not n0_is_upside_down and p0.y + 1 == p1.y then slabpos = p0 slabnode = n0 elseif n1.name == "stairs:slab_" .. subname then slabpos = p1 slabnode = n1 end if slabpos then -- Remove the slab at slabpos minetest.remove_node(slabpos) -- Make a fake stack of a single item and try to place it local fakestack = ItemStack(recipeitem) fakestack:set_count(itemstack:get_count()) pointed_thing.above = slabpos local success fakestack, success = minetest.item_place(fakestack, placer, pointed_thing) -- If the item was taken from the fake stack, decrement original if success then itemstack:set_count(fakestack:get_count()) -- Else put old node back else minetest.set_node(slabpos, slabnode) end return itemstack end -- Upside down slabs if p0.y - 1 == p1.y then -- Turn into full block if pointing at a existing slab if n0_is_upside_down then -- Remove the slab at the position of the slab minetest.remove_node(p0) -- Make a fake stack of a single item and try to place it local fakestack = ItemStack(recipeitem) fakestack:set_count(itemstack:get_count()) pointed_thing.above = p0 local success fakestack, success = minetest.item_place(fakestack, placer, pointed_thing) -- If the item was taken from the fake stack, decrement original if success then itemstack:set_count(fakestack:get_count()) -- Else put old node back else minetest.set_node(p0, n0) end return itemstack end -- Place upside down slab param2 = 20 end -- If pointing at the side of a upside down slab if n0_is_upside_down and p0.y + 1 ~= p1.y then param2 = 20 end return minetest.item_place(itemstack, placer, pointed_thing, param2) end, }) -- for replace ABM if replace then minetest.register_node(":stairs:slab_" .. subname .. "upside_down", { replace_name = "stairs:slab_".. subname, groups = {slabs_replace = 1} , }) end minetest.register_craft({ output = 'stairs:slab_' .. subname .. ' 6', recipe = { {recipeitem, recipeitem, recipeitem} , } , }) end -- Optionally replace old "upside_down" nodes with new param2 versions. -- Disabled by default. if replace then minetest.register_abm({ nodenames = {"group:slabs_replace" } , interval = 16, chance = 1, action = function(pos, node) node.name = minetest.registered_nodes[node.name].replace_name node.param2 = node.param2 + 20 if node.param2 == 21 then node.param2 = 23 elseif node.param2 == 23 then node.param2 = 21 end minetest.set_node(pos, node) end, }) end -- From Stairs Redo -- process textures local set_textures = function(images) local stair_images = {} for i, image in ipairs(images) do if type(image) == "string" then stair_images[i] = { name = image, backface_culling = true, align_style = "world", } elseif image.backface_culling == nil then -- override using any other value if stair_images[i].backface_culling == nil then stair_images[i].backface_culling = true end if stair_images[i].align_style == nil then stair_images[i].align_style = "world" end end end return stair_images end -- From Stairs Redo -- Node will be called stairs:stair_outer_ function stairs.register_stair_outer (subname, recipeitem, groups, images, description, snds, alpha) local stair_images = set_textures(images) local new_groups = table.copy(groups) new_groups.stair = 1 minetest.register_node(":stairs:stair_outer_" .. subname, { description = "Outer " .. description .. " Stair", drawtype = "nodebox", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", light_source = nodelight (recipeitem) , is_ground_content = false, use_texture_alpha = alpha, groups = new_groups, sounds = snds, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0, 0.5, 0.5} , } , } , on_place = function(itemstack, placer, pointed_thing) return stair_place(itemstack, placer, pointed_thing, "stairs:stair_outer_" .. subname) end, }) -- add alias for old stairs redo name minetest.register_alias("stairs:corner_" .. subname, "stairs:stair_outer_" .. subname) -- if no recipe item provided then skip craft recipes if not recipeitem then return end -- corner stair recipe minetest.register_craft({ output = "stairs:stair_outer_" .. subname .. " 6", recipe = { {"", "", "" } , {"", recipeitem, "" } , {recipeitem, recipeitem, recipeitem} , } , }) -- corner stair to original material recipe minetest.register_craft({ output = recipeitem .. " 2", recipe = { {"stairs:stair_outer_" .. subname, "stairs:stair_outer_" .. subname} , {"stairs:stair_outer_" .. subname, "stairs:stair_outer_" .. subname} , } , }) end -- From Stairs Redo -- compatibility function for previous stairs:corner_ function stairs.register_corner(subname, recipeitem, groups, images, description, snds, alpha) stairs.register_stair_outer (subname, recipeitem, groups, images, description, snds, alpha) end -- From Stairs Redo -- Node will be called stairs:stair_inner_ function stairs.register_stair_inner(subname, recipeitem, groups, images, description, snds, alpha) local stair_images = set_textures(images) local new_groups = table.copy(groups) new_groups.stair = 1 minetest.register_node(":stairs:stair_inner_" .. subname, { description = "Inner " .. description .. " Stair", drawtype = "nodebox", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", light_source = nodelight (recipeitem) , is_ground_content = false, use_texture_alpha = alpha, groups = new_groups, sounds = snds, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0.5, 0.5, 0.5} , {-0.5, 0, -0.5, 0, 0.5, 0} , } , } , on_place = function(itemstack, placer, pointed_thing) return stair_place(itemstack, placer, pointed_thing, "stairs:stair_inner_" .. subname) end, }) -- add alias for old stairs redo name minetest.register_alias("stairs:invcorner_" .. subname, "stairs:stair_inner_" .. subname) -- if no recipe item provided then skip craft recipes if not recipeitem then return end -- inside corner stair recipe minetest.register_craft({ output = "stairs:stair_inner_" .. subname .. " 9", recipe = { {recipeitem, recipeitem, "" } , {recipeitem, recipeitem, recipeitem} , {recipeitem, recipeitem, recipeitem} , } , }) -- inside corner stair to original material recipe minetest.register_craft({ output = recipeitem .. " 3", recipe = { {"stairs:stair_inner_" .. subname, "stairs:stair_inner_" .. subname} , {"stairs:stair_inner_" .. subname, "stairs:stair_inner_" .. subname} , } , }) end -- From Stairs Redo -- compatibility function for previous stairs:invcorner_ function stairs.register_invcorner(subname, recipeitem, groups, images, description, snds, alpha) stairs.register_stair_inner(subname, recipeitem, groups, images, description, snds, alpha) end -- From Stairs Redo -- Node will be called stairs:slope_ function stairs.register_slope (subname, recipeitem, groups, images, description, snds, alpha) local stair_images = set_textures (images) local new_groups = table.copy (groups) new_groups.stair = 1 minetest.register_node(":stairs:slope_" .. subname, { description = description .. " Slope", drawtype = "mesh", mesh = "stairs_slope.obj", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", light_source = nodelight (recipeitem) , is_ground_content = false, use_texture_alpha = alpha, groups = new_groups, sounds = snds, selection_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0.5, 0.5, 0.5} , } , } , collision_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0, 0.5} , {-0.5, 0, 0, 0.5, 0.5, 0.5} , } , } , on_place = function(itemstack, placer, pointed_thing) return stair_place(itemstack, placer, pointed_thing, "stairs:slope_" .. subname) end, }) -- slope recipe minetest.register_craft({ output = "stairs:slope_" .. subname .. " 6", recipe = { {recipeitem, "", "" } , {recipeitem, recipeitem, "" } , } , }) -- slope to original material recipe minetest.register_craft({ output = recipeitem, recipe = { {"stairs:slope_" .. subname, "stairs:slope_" .. subname} , } , }) end -- Stair/slab registration function. -- Nodes will be called stairs:{stair,slab}_ function stairs.register_stair_and_slab(subname, recipeitem, groups, images, desc_stair, desc_slab, sounds) stairs.register_stair(subname, recipeitem, groups, images, desc_stair, sounds) stairs.register_slab(subname, recipeitem, groups, images, desc_slab, sounds) end -- Nodes will be called -- stairs:{stair,slab,corner,invcorner,slope}_ function stairs.register_all (subname, recipeitem, groups, images, desc, snds, alpha) stairs.register_stair (subname, recipeitem, groups, images, desc .. " Stair" , snds, alpha) stairs.register_slab (subname, recipeitem, groups, images, desc .. " Slab" , snds, alpha) stairs.register_corner (subname, recipeitem, groups, images, desc, snds, alpha) stairs.register_invcorner (subname, recipeitem, groups, images, desc, snds, alpha) stairs.register_slope (subname, recipeitem, groups, images, desc, snds, alpha) end -- =================================================================== stairs.register_if_appropriate = function (nodepart, fromnode, groups, imgtab, desc, sndtab) local neednode = "stairs:stair_" .. nodepart if neednode ~= nil and ocutil.node_exists (neednode) then return end if ocutil.node_missing (fromnode) then return end stairs.register_all (nodepart, fromnode, groups, imgtab, desc, sndtab) end -- =================================================================== stairs.default_if_appropriate = function (nodepart, groups, desc, sndtab) local fromnode = "default:" .. nodepart if ocutil.node_missing (fromnode) then return end local neednode = "stairs:stair_" .. nodepart local defpath = minetest.get_modpath ("default") local textdir = defpath .. "/textures/" local imgfile = "default_" .. nodepart .. ".png" local imgpath = textdir .. imgfile if ocutil.file_missing (imgpath) then minetest.log ("action", "[stairs] Missing image: " .. imgpath) return end if ocutil.node_exists (neednode) then if ocutil.node_missing ("stairs:outer_" .. nodepart) then stairs.register_corner (nodepart, fromnode, groups, { imgfile }, desc, sndtab) end if ocutil.node_missing ("stairs:slab_" .. nodepart) then stairs.register_slab (nodepart, fromnode, groups, { imgfile }, desc, sndtab) end if ocutil.node_missing ("stairs:slope_" .. nodepart) then stairs.register_slope (nodepart, fromnode, groups, { imgfile }, desc, sndtab) end else stairs.register_all (nodepart, fromnode, groups, { imgfile }, desc, sndtab) end end -- =================================================================== stairs.stone_if_appropriate = function (nodepart, groups, desc) stairs.default_if_appropriate (nodepart, groups, desc, stairs.snd_stone) end -- =================================================================== stairs.wood_if_appropriate = function (nodepart, groups, desc) stairs.default_if_appropriate (nodepart, groups, desc, stairs.snd_wood) end -- =================================================================== -- Register initial stairs, slabs, etc.: Stone-like except bricks. stairs.stone_if_appropriate ("bronzeblock" , { cracky=1, level=2 } , "Bronze Block" ) stairs.stone_if_appropriate ("coalblock" , { cracky=3 } , "Coal Block" ) stairs.stone_if_appropriate ("cobble" , { cracky=3 } , "Cobble" ) stairs.stone_if_appropriate ("copperblock" , { cracky=1, level=2 } , "Copper Block" ) stairs.stone_if_appropriate ("coral_skeleton" , { cracky=3 } , "Coral Skeleton" ) stairs.stone_if_appropriate ("desert_cobble" , { cracky=3 } , "Desert Cobble" ) stairs.stone_if_appropriate ("desert_sandstone_block" , { cracky=2 } , "Desert Sandstone Block" ) stairs.stone_if_appropriate ("desert_stone" , { cracky=3 } , "Desert Stone" ) stairs.stone_if_appropriate ("desert_stone_block" , { cracky=3 } , "Desert Stone" ) stairs.stone_if_appropriate ("diamondblock" , { cracky=1 } , "Diamond Block" ) stairs.stone_if_appropriate ("goldblock" , { cracky=1 } , "Gold Block" ) stairs.stone_if_appropriate ("mese" , { cracky=3 } , "Mese Block" ) stairs.stone_if_appropriate ("mossycobble" , { cracky=3 } , "Mossy Cobble" ) stairs.stone_if_appropriate ("obsidian" , { cracky=1, level=2 } , "Obsidian" ) stairs.stone_if_appropriate ("obsidian_block" , { cracky=1, level=2 } , "Obsidian Block" ) stairs.stone_if_appropriate ("sandstone" , { crumbly=1, cracky=3 } , "Sandstone" ) stairs.stone_if_appropriate ("sandstone_block" , { cracky=3 } , "Sandstone Block" ) stairs.stone_if_appropriate ("silver_sandstone" , { cracky=3 } , "Silver Sandstone" ) stairs.stone_if_appropriate ("silver_sandstone_block" , { cracky=3 } , "Silver Sandstone Block" ) stairs.stone_if_appropriate ("stone" , { cracky=3, stone=1 } , "Stone" ) stairs.stone_if_appropriate ("stone_block" , { cracky=2 } , "Stone Block" ) stairs.stone_if_appropriate ("steelblock" , { cracky=1, level=2 } , "Steel Block" ) -- =================================================================== -- Register initial stairs, slabs, etc.: Stone-like bricks. stairs.stone_if_appropriate ("brick" , { cracky=3 } , "Brick" ) stairs.stone_if_appropriate ("desert_sandstone_brick" , { cracky=2 } , "Desert Sandstone Brick" ) stairs.stone_if_appropriate ("desert_stonebrick" , { cracky=3 } , "Desert Stone Brick" ) stairs.stone_if_appropriate ("obsidianbrick" , { cracky=1, level=2 } , "Obsidian Brick" ) stairs.stone_if_appropriate ("sandstonebrick" , { cracky=2 } , "Sandstone Brick" ) stairs.stone_if_appropriate ("silver_sandstone_brick" , { cracky=3 } , "Silver Sandstone Brick" ) stairs.stone_if_appropriate ("stonebrick" , { cracky=3 } , "Stone Brick" ) -- =================================================================== -- Initial stairs, slabs, etc.: "default" woods. local tree_parts = { "trunk", "wood" } local wood_types = { "acacia" , "aspen" , "beech" , "birch" , "jungle" , "maple" , "pine" , "poplar" , "sequoia" , "spruce" , "plain" , } for i, wood_type in ipairs (wood_types) do for j, tree_part in ipairs (tree_parts) do local node = wood_type .. "_" .. tree_part local desc = ocutil.first_to_upper (wood_type) .. " " .. ocutil.first_to_upper (tree_part) if node ~= "jungle_trunk" and node ~= "plain_trunk" then -- Handle some special cases if node == "jungle_wood" then node = "junglewood" end if node == "plain_wood" then node = "wood" desc = "Wood" end stairs.wood_if_appropriate (node, { choppy=2, oddly_breakable_by_hand=2, flammable=3 } , desc) end end end -- =================================================================== -- Initial stairs, slabs, etc.: Default tree. neednode = "stairs:stair_tree" fromnode = "default:tree" if ocutil.node_missing (neednode) and ocutil.node_exists (fromnode) then stairs.register_all ("tree" , fromnode , { choppy=2, flammable=3, oddly_breakable_by_hand=2 } , { "default_tree.png" } , "Tree" , default.node_sound_wood_defaults()) end -- =================================================================== -- Initial stairs, slabs, etc.: Misc. neednode = "stairs:stair_silver_block" fromnode = "moreores:silver_block" if ocutil.node_missing (neednode) and ocutil.node_exists (fromnode) then stairs.register_all ("silver_block" , fromnode , { snappy=1, cracky=1, melty=2, level=2 } , { "moreores_silver_block.png" } , "Silver Sandstone Block" , default.node_sound_stone_defaults()) end -- =================================================================== nodepart = "ice" fromnode = "default:" .. nodepart -- if ocutil.node_exists (fromnode) then stairs.register_all (nodepart, fromnode, { cracky=3, cools_lava=1, slippery=3 } , { "default_ice.png" } , "Ice" , stairs.snd_glass) end -- =================================================================== neednode = "stairs:slab_snowblock" fromnode = "default:snowblock" if ocutil.node_missing (neednode) and ocutil.node_exists (fromnode) then stairs.register_all ("snowblock" , fromnode , { crumbly=3, puts_out_fire=1, cools_lava=1, snowy=1 } , { "default_snow.png" } , "Snow Block" , default.node_sound_dirt_defaults ({ footstep = { name="default_snow_footstep" , gain=0.15 } , dug = { name="default_snow_footstep" , gain=0.20 } , dig = { name="default_snow_footstep" , gain=0.20 } , })) end -- =================================================================== nodepart = "straw" fromnode = "farming:" .. nodepart -- if ocutil.node_exists (fromnode) then stairs.register_all (nodepart, fromnode, { snappy=3, flammable=4 } , { "farming_straw.png" } , "Straw" , stairs.snd_leaves) end -- =================================================================== nodepart = "glass" fromnode = "default:" .. nodepart -- if ocutil.node_exists (fromnode) then stairs.register_all (nodepart, fromnode, { cracky=3, oddly_breakable_by_hand=3 } , { "default_glass.png" } , "Glass" , stairs.snd_glass) end -- =================================================================== nodepart = "obsidian_glass" fromnode = "default:" .. nodepart -- if ocutil.node_exists (fromnode) then stairs.register_all (nodepart, fromnode, { cracky=2 } , { "default_obsidian_glass.png" } , "Obsidian Glass" , stairs.snd_glass) end -- =================================================================== nodepart = "cactus" fromnode = "default:" .. nodepart -- if ocutil.node_exists (fromnode) then stairs.register_all (nodepart, fromnode, { cracky=2 } , { "default_cactus_side.png" } , "Cactus" , stairs.snd_leaves) end -- =================================================================== local sra = ocutil.safe_register_alias sra ("stairs:slope_coal" , "stairs:slope_coalblock" ) sra ("stairs:stair_pinewood" , "stairs:stair_pine_wood" ) sra ("stairs:slab_pinewood" , "stairs:slab_pine_wood" ) -- =================================================================== -- End of module.