-- stairs/init.lua -- Minetest 0.4 mod: stairs -- See README.txt for licensing and other information. -- Global namespace for functions stairs = {} -- Load support for MT game translation. local S = minetest.get_translator("stairs") -- Register aliases for new pine node names minetest.register_alias("stairs:stair_pinewood", "stairs:stair_pine_wood") minetest.register_alias("stairs:slab_pinewood", "stairs:slab_pine_wood") -- Get setting for replace ABM local replace = minetest.settings:get_bool("enable_stairs_replace_abm") local function rotate_and_place(itemstack, placer, pointed_thing) local p0 = pointed_thing.under local p1 = pointed_thing.above local param2 = 0 if placer then local placer_pos = placer:get_pos() if placer_pos then param2 = minetest.dir_to_facedir(vector.subtract(p1, placer_pos)) end local finepos = minetest.pointed_thing_to_face_pos(placer, pointed_thing) local fpos = finepos.y % 1 if p0.y - 1 == p1.y or (fpos > 0 and fpos < 0.5) or (fpos < -0.5 and fpos > -0.999999999) then param2 = param2 + 20 if param2 == 21 then param2 = 23 elseif param2 == 23 then param2 = 21 end end end return minetest.item_place(itemstack, placer, pointed_thing, param2) end local function warn_if_exists(nodename) if minetest.registered_nodes[nodename] then minetest.log("warning", "Overwriting stairs node: " .. nodename) end end -- Register stair -- Node will be called stairs:stair_ function stairs.register_stair(subname, recipeitem, groups, images, description, sounds, worldaligntex) -- Set backface culling and world-aligned textures local stair_images = {} for i, image in ipairs(images) do if type(image) == "string" then stair_images[i] = { name = image, backface_culling = true, } if worldaligntex then stair_images[i].align_style = "world" end else stair_images[i] = table.copy(image) if stair_images[i].backface_culling == nil then stair_images[i].backface_culling = true end if worldaligntex and stair_images[i].align_style == nil then stair_images[i].align_style = "world" end end end local new_groups = table.copy(groups) new_groups.stair = 1 warn_if_exists("stairs:stair_" .. subname) minetest.register_node(":stairs:stair_" .. subname, { description = description, drawtype = "nodebox", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", is_ground_content = false, groups = new_groups, sounds = sounds, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5}, {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5}, }, }, on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end return rotate_and_place(itemstack, placer, pointed_thing) 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 if recipeitem then -- Recipe matches appearence in inventory minetest.register_craft({ output = "stairs:stair_" .. subname .. " 8", recipe = { {"", "", recipeitem}, {"", recipeitem, recipeitem}, {recipeitem, recipeitem, recipeitem}, }, }) -- Use stairs to craft full blocks again (1:1) minetest.register_craft({ output = recipeitem .. " 3", recipe = { {"stairs:stair_" .. subname, "stairs:stair_" .. subname}, {"stairs:stair_" .. subname, "stairs:stair_" .. subname}, }, }) -- Fuel local baseburntime = minetest.get_craft_result({ method = "fuel", width = 1, items = {recipeitem} }).time if baseburntime > 0 then minetest.register_craft({ type = "fuel", recipe = "stairs:stair_" .. subname, burntime = math.floor(baseburntime * 0.75), }) end end end -- Register slab -- Node will be called stairs:slab_ function stairs.register_slab(subname, recipeitem, groups, images, description, sounds, worldaligntex) -- Set world-aligned textures local slab_images = {} for i, image in ipairs(images) do if type(image) == "string" then slab_images[i] = { name = image, } if worldaligntex then slab_images[i].align_style = "world" end else slab_images[i] = table.copy(image) if worldaligntex and image.align_style == nil then slab_images[i].align_style = "world" end end end local new_groups = table.copy(groups) new_groups.slab = 1 warn_if_exists("stairs:slab_" .. subname) minetest.register_node(":stairs:slab_" .. subname, { description = description, drawtype = "nodebox", tiles = slab_images, paramtype = "light", paramtype2 = "facedir", is_ground_content = false, groups = new_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) local under = minetest.get_node(pointed_thing.under) local wield_item = itemstack:get_name() local player_name = placer and placer:get_player_name() or "" local creative_enabled = (creative and creative.is_enabled_for and creative.is_enabled_for(player_name)) if under and under.name:find("^stairs:slab_") then -- place slab using under node orientation local dir = minetest.dir_to_facedir(vector.subtract( pointed_thing.above, pointed_thing.under), true) local p2 = under.param2 -- Placing a slab on an upside down slab should make it right-side up. if p2 >= 20 and dir == 8 then p2 = p2 - 20 -- same for the opposite case: slab below normal slab elseif p2 <= 3 and dir == 4 then p2 = p2 + 20 end -- else attempt to place node with proper param2 minetest.item_place_node(ItemStack(wield_item), placer, pointed_thing, p2) if not creative_enabled then itemstack:take_item() end return itemstack else return rotate_and_place(itemstack, placer, pointed_thing) end 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 if recipeitem then minetest.register_craft({ output = "stairs:slab_" .. subname .. " 6", recipe = { {recipeitem, recipeitem, recipeitem}, }, }) -- Use 2 slabs to craft a full block again (1:1) minetest.register_craft({ output = recipeitem, recipe = { {"stairs:slab_" .. subname}, {"stairs:slab_" .. subname}, }, }) -- Fuel local baseburntime = minetest.get_craft_result({ method = "fuel", width = 1, items = {recipeitem} }).time if baseburntime > 0 then minetest.register_craft({ type = "fuel", recipe = "stairs:slab_" .. subname, burntime = math.floor(baseburntime * 0.5), }) end end end -- Optionally replace old "upside_down" nodes with new param2 versions. -- Disabled by default. if replace then minetest.register_abm({ label = "Slab replace", 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 -- Register inner stair -- Node will be called stairs:stair_inner_ function stairs.register_stair_inner(subname, recipeitem, groups, images, description, sounds, worldaligntex) -- Set backface culling and world-aligned textures local stair_images = {} for i, image in ipairs(images) do if type(image) == "string" then stair_images[i] = { name = image, backface_culling = true, } if worldaligntex then stair_images[i].align_style = "world" end else stair_images[i] = table.copy(image) if stair_images[i].backface_culling == nil then stair_images[i].backface_culling = true end if worldaligntex and stair_images[i].align_style == nil then stair_images[i].align_style = "world" end end end local new_groups = table.copy(groups) new_groups.stair = 1 warn_if_exists("stairs:stair_inner_" .. subname) minetest.register_node(":stairs:stair_inner_" .. subname, { description = S("Inner @1", description), drawtype = "nodebox", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", is_ground_content = false, groups = new_groups, sounds = sounds, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5}, {-0.5, 0.0, 0.0, 0.5, 0.5, 0.5}, {-0.5, 0.0, -0.5, 0.0, 0.5, 0.0}, }, }, on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end return rotate_and_place(itemstack, placer, pointed_thing) end, }) if recipeitem then minetest.register_craft({ output = "stairs:stair_inner_" .. subname .. " 7", recipe = { {"", recipeitem, ""}, {recipeitem, "", recipeitem}, {recipeitem, recipeitem, recipeitem}, }, }) -- Fuel local baseburntime = minetest.get_craft_result({ method = "fuel", width = 1, items = {recipeitem} }).time if baseburntime > 0 then minetest.register_craft({ type = "fuel", recipe = "stairs:stair_inner_" .. subname, burntime = math.floor(baseburntime * 0.875), }) end end end -- Register outer stair -- Node will be called stairs:stair_outer_ function stairs.register_stair_outer(subname, recipeitem, groups, images, description, sounds, worldaligntex) -- Set backface culling and world-aligned textures local stair_images = {} for i, image in ipairs(images) do if type(image) == "string" then stair_images[i] = { name = image, backface_culling = true, } if worldaligntex then stair_images[i].align_style = "world" end else stair_images[i] = table.copy(image) if stair_images[i].backface_culling == nil then stair_images[i].backface_culling = true end if worldaligntex and stair_images[i].align_style == nil then stair_images[i].align_style = "world" end end end local new_groups = table.copy(groups) new_groups.stair = 1 warn_if_exists("stairs:stair_outer_" .. subname) minetest.register_node(":stairs:stair_outer_" .. subname, { description = S("Outer @1", description), drawtype = "nodebox", tiles = stair_images, paramtype = "light", paramtype2 = "facedir", is_ground_content = false, groups = new_groups, sounds = sounds, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 0.0, 0.5}, {-0.5, 0.0, 0.0, 0.0, 0.5, 0.5}, }, }, on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end return rotate_and_place(itemstack, placer, pointed_thing) end, }) if recipeitem then minetest.register_craft({ output = "stairs:stair_outer_" .. subname .. " 6", recipe = { {"", recipeitem, ""}, {recipeitem, recipeitem, recipeitem}, }, }) -- Fuel local baseburntime = minetest.get_craft_result({ method = "fuel", width = 1, items = {recipeitem} }).time if baseburntime > 0 then minetest.register_craft({ type = "fuel", recipe = "stairs:stair_outer_" .. subname, burntime = math.floor(baseburntime * 0.625), }) end end 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, worldaligntex) stairs.register_stair(subname, recipeitem, groups, images, desc_stair, sounds, worldaligntex) stairs.register_stair_inner(subname, recipeitem, groups, images, desc_stair, sounds, worldaligntex) stairs.register_stair_outer(subname, recipeitem, groups, images, desc_stair, sounds, worldaligntex) stairs.register_slab(subname, recipeitem, groups, images, desc_slab, sounds, worldaligntex) end -- Register default stairs and slabs stairs.register_stair_and_slab( "wood", "default:wood", {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, {"default_wood.png"}, S("Wooden Stair"), S("Wooden Slab"), default.node_sound_wood_defaults(), false ) stairs.register_stair_and_slab( "junglewood", "default:junglewood", {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, {"default_junglewood.png"}, S("Jungle Wood Stair"), S("Jungle Wood Slab"), default.node_sound_wood_defaults(), false ) stairs.register_stair_and_slab( "pine_wood", "default:pine_wood", {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3}, {"default_pine_wood.png"}, S("Pine Wood Stair"), S("Pine Wood Slab"), default.node_sound_wood_defaults(), false ) stairs.register_stair_and_slab( "acacia_wood", "default:acacia_wood", {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, {"default_acacia_wood.png"}, S("Acacia Wood Stair"), S("Acacia Wood Slab"), default.node_sound_wood_defaults(), false ) stairs.register_stair_and_slab( "aspen_wood", "default:aspen_wood", {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3}, {"default_aspen_wood.png"}, S("Aspen Wood Stair"), S("Aspen Wood Slab"), default.node_sound_wood_defaults(), false ) stairs.register_stair_and_slab( "stone", "default:stone", {cracky = 3}, {"default_stone.png"}, S("Stone Stair"), S("Stone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "cobble", "default:cobble", {cracky = 3}, {"default_cobble.png"}, S("Cobblestone Stair"), S("Cobblestone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "mossycobble", "default:mossycobble", {cracky = 3}, {"default_mossycobble.png"}, S("Mossy Cobblestone Stair"), S("Mossy Cobblestone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "stonebrick", "default:stonebrick", {cracky = 2}, {"default_stone_brick.png"}, S("Stone Brick Stair"), S("Stone Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "stone_block", "default:stone_block", {cracky = 2}, {"default_stone_block.png"}, S("Stone Block Stair"), S("Stone Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "desert_stone", "default:desert_stone", {cracky = 3}, {"default_desert_stone.png"}, S("Desert Stone Stair"), S("Desert Stone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "desert_cobble", "default:desert_cobble", {cracky = 3}, {"default_desert_cobble.png"}, S("Desert Cobblestone Stair"), S("Desert Cobblestone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "desert_stonebrick", "default:desert_stonebrick", {cracky = 2}, {"default_desert_stone_brick.png"}, S("Desert Stone Brick Stair"), S("Desert Stone Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "desert_stone_block", "default:desert_stone_block", {cracky = 2}, {"default_desert_stone_block.png"}, S("Desert Stone Block Stair"), S("Desert Stone Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "sandstone", "default:sandstone", {crumbly = 1, cracky = 3}, {"default_sandstone.png"}, S("Sandstone Stair"), S("Sandstone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "sandstonebrick", "default:sandstonebrick", {cracky = 2}, {"default_sandstone_brick.png"}, S("Sandstone Brick Stair"), S("Sandstone Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "sandstone_block", "default:sandstone_block", {cracky = 2}, {"default_sandstone_block.png"}, S("Sandstone Block Stair"), S("Sandstone Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "desert_sandstone", "default:desert_sandstone", {crumbly = 1, cracky = 3}, {"default_desert_sandstone.png"}, S("Desert Sandstone Stair"), S("Desert Sandstone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "desert_sandstone_brick", "default:desert_sandstone_brick", {cracky = 2}, {"default_desert_sandstone_brick.png"}, S("Desert Sandstone Brick Stair"), S("Desert Sandstone Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "desert_sandstone_block", "default:desert_sandstone_block", {cracky = 2}, {"default_desert_sandstone_block.png"}, S("Desert Sandstone Block Stair"), S("Desert Sandstone Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "silver_sandstone", "default:silver_sandstone", {crumbly = 1, cracky = 3}, {"default_silver_sandstone.png"}, S("Silver Sandstone Stair"), S("Silver Sandstone Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "silver_sandstone_brick", "default:silver_sandstone_brick", {cracky = 2}, {"default_silver_sandstone_brick.png"}, S("Silver Sandstone Brick Stair"), S("Silver Sandstone Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "silver_sandstone_block", "default:silver_sandstone_block", {cracky = 2}, {"default_silver_sandstone_block.png"}, S("Silver Sandstone Block Stair"), S("Silver Sandstone Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "obsidian", "default:obsidian", {cracky = 1, level = 2}, {"default_obsidian.png"}, S("Obsidian Stair"), S("Obsidian Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "obsidianbrick", "default:obsidianbrick", {cracky = 1, level = 2}, {"default_obsidian_brick.png"}, S("Obsidian Brick Stair"), S("Obsidian Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "obsidian_block", "default:obsidian_block", {cracky = 1, level = 2}, {"default_obsidian_block.png"}, S("Obsidian Block Stair"), S("Obsidian Block Slab"), default.node_sound_stone_defaults(), true ) stairs.register_stair_and_slab( "brick", "default:brick", {cracky = 3}, {"default_brick.png"}, S("Brick Stair"), S("Brick Slab"), default.node_sound_stone_defaults(), false ) stairs.register_stair_and_slab( "steelblock", "default:steelblock", {cracky = 1, level = 2}, {"default_steel_block.png"}, S("Steel Block Stair"), S("Steel Block Slab"), default.node_sound_metal_defaults(), true ) stairs.register_stair_and_slab( "tinblock", "default:tinblock", {cracky = 1, level = 2}, {"default_tin_block.png"}, S("Tin Block Stair"), S("Tin Block Slab"), default.node_sound_metal_defaults(), true ) stairs.register_stair_and_slab( "copperblock", "default:copperblock", {cracky = 1, level = 2}, {"default_copper_block.png"}, S("Copper Block Stair"), S("Copper Block Slab"), default.node_sound_metal_defaults(), true ) stairs.register_stair_and_slab( "bronzeblock", "default:bronzeblock", {cracky = 1, level = 2}, {"default_bronze_block.png"}, S("Bronze Block Stair"), S("Bronze Block Slab"), default.node_sound_metal_defaults(), true ) stairs.register_stair_and_slab( "goldblock", "default:goldblock", {cracky = 1}, {"default_gold_block.png"}, S("Gold Block Stair"), S("Gold Block Slab"), default.node_sound_metal_defaults(), true ) stairs.register_stair_and_slab( "ice", "default:ice", {cracky = 3, cools_lava = 1, slippery = 3}, {"default_ice.png"}, S("Ice Stair"), S("Ice Slab"), default.node_sound_glass_defaults(), true ) stairs.register_stair_and_slab( "snowblock", "default:snowblock", {crumbly = 3, cools_lava = 1, snowy = 1}, {"default_snow.png"}, S("Snow Block Stair"), S("Snow Block Slab"), default.node_sound_snow_defaults(), true ) -- Glass stair nodes need to be registered individually to utilize specialized textures. stairs.register_stair( "glass", "default:glass", {cracky = 3}, {"stairs_glass_split.png", "default_glass.png", "stairs_glass_stairside.png^[transformFX", "stairs_glass_stairside.png", "default_glass.png", "stairs_glass_split.png"}, S("Glass Stair"), default.node_sound_glass_defaults(), false ) stairs.register_slab( "glass", "default:glass", {cracky = 3}, {"default_glass.png", "default_glass.png", "stairs_glass_split.png"}, S("Glass Slab"), default.node_sound_glass_defaults(), false ) stairs.register_stair_inner( "glass", "default:glass", {cracky = 3}, {"stairs_glass_stairside.png^[transformR270", "default_glass.png", "stairs_glass_stairside.png^[transformFX", "default_glass.png", "default_glass.png", "stairs_glass_stairside.png"}, S("Glass Stair"), default.node_sound_glass_defaults(), false ) stairs.register_stair_outer( "glass", "default:glass", {cracky = 3}, {"stairs_glass_stairside.png^[transformR90", "default_glass.png", "stairs_glass_outer_stairside.png", "stairs_glass_stairside.png", "stairs_glass_stairside.png^[transformR90","stairs_glass_outer_stairside.png"}, S("Glass Stair"), default.node_sound_glass_defaults(), false ) stairs.register_stair( "obsidian_glass", "default:obsidian_glass", {cracky = 3}, {"stairs_obsidian_glass_split.png", "default_obsidian_glass.png", "stairs_obsidian_glass_stairside.png^[transformFX", "stairs_obsidian_glass_stairside.png", "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"}, S("Obsidian Glass Stair"), default.node_sound_glass_defaults(), false ) stairs.register_slab( "obsidian_glass", "default:obsidian_glass", {cracky = 3}, {"default_obsidian_glass.png", "default_obsidian_glass.png", "stairs_obsidian_glass_split.png"}, S("Obsidian Glass Slab"), default.node_sound_glass_defaults(), false ) stairs.register_stair_inner( "obsidian_glass", "default:obsidian_glass", {cracky = 3}, {"stairs_obsidian_glass_stairside.png^[transformR270", "default_obsidian_glass.png", "stairs_obsidian_glass_stairside.png^[transformFX", "default_obsidian_glass.png", "default_obsidian_glass.png", "stairs_obsidian_glass_stairside.png"}, S("Obsidian Glass Stair"), default.node_sound_glass_defaults(), false ) stairs.register_stair_outer( "obsidian_glass", "default:obsidian_glass", {cracky = 3}, {"stairs_obsidian_glass_stairside.png^[transformR90", "default_obsidian_glass.png", "stairs_obsidian_glass_outer_stairside.png", "stairs_obsidian_glass_stairside.png", "stairs_obsidian_glass_stairside.png^[transformR90","stairs_obsidian_glass_outer_stairside.png"}, S("Obsidian Glass Stair"), default.node_sound_glass_defaults(), false )