459 lines
13 KiB
Lua
459 lines
13 KiB
Lua
-- Lazarr! mod: lzr_stairs
|
|
-- See README.txt for licensing and other information.
|
|
|
|
-- Global namespace for functions
|
|
lzr_stairs = {}
|
|
|
|
local S = minetest.get_translator("lzr_stairs")
|
|
|
|
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
|
|
|
|
local def = itemstack:get_definition()
|
|
if def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir" then
|
|
-- Flip node vertically if placed at upper half of node
|
|
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
|
|
end
|
|
return minetest.item_place(itemstack, placer, pointed_thing, param2)
|
|
end
|
|
|
|
|
|
-- Register stair.
|
|
-- CAN be rotated.
|
|
-- Node will be called lzr_stairs:stair_<subname>
|
|
|
|
function lzr_stairs.register_stair(subname, recipeitem, groups, images, description,
|
|
sounds, worldaligntex, vertical_rotation)
|
|
local src_def = minetest.registered_nodes[recipeitem]
|
|
|
|
-- 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
|
|
new_groups.rotatable = 3
|
|
local paramtype2
|
|
if vertical_rotation == false then
|
|
paramtype2 = "4dir"
|
|
else
|
|
paramtype2 = "facedir"
|
|
end
|
|
minetest.register_node(":lzr_stairs:stair_" .. subname, {
|
|
description = description,
|
|
drawtype = "nodebox",
|
|
tiles = stair_images,
|
|
use_texture_alpha = src_def and src_def.use_texture_alpha,
|
|
paramtype = "light",
|
|
paramtype2 = paramtype2,
|
|
is_ground_content = false,
|
|
groups = new_groups,
|
|
sounds = sounds,
|
|
node_box = {
|
|
-- This stair nodebox is unusual and differs from most other Luanti games.
|
|
-- The segments have been made slightly different so that a laser will alway
|
|
-- be completely blocked off from any direction.
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.5, -0.5, -0.5, 0.5, 1/16, 0.5},
|
|
{-0.5, 1/16, -1/16, 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,
|
|
})
|
|
end
|
|
|
|
|
|
-- Register slab. (includes slab for bottom and top part of node)
|
|
-- The slab can be rotated to move to the bottom or top of the node.
|
|
-- Sideways slabs do not exist.
|
|
--
|
|
-- Registers multiple nodes via lzr_laser.register_element,
|
|
-- using the basename `lzr_stairs:slab_<subname>`.
|
|
-- In practice, this will register the following nodes:
|
|
-- * lzr_stairs:slab_<subname>_fixed for the bottom slab,
|
|
-- * lzr_stairs:slab_top_<subname>_fixed for the top slab and
|
|
-- * lzr_stairs:slab_<subname>_fixed_on_<colorcode> for the bottom slab with a laser from above
|
|
-- * lzr_stairs:slab_top_<subname>_fixed_on_<colorcode> for the top slabs with a laser from below
|
|
--
|
|
-- (where <colorcode> being the laser colorcode)
|
|
function lzr_stairs.register_slab(subname, recipeitem, groups, images, description,
|
|
sounds, worldaligntex, images_top)
|
|
local src_def = minetest.registered_nodes[recipeitem]
|
|
|
|
local parse_images = function(images)
|
|
-- Use first tile only, for slab mesh
|
|
local slab_images = table.copy(images)
|
|
if type(slab_images[1]) == "string" then
|
|
-- Add backface culling because otherwise z-fighting occurs when
|
|
-- inside the node
|
|
slab_images[1] = { name = slab_images[1], backface_culling = true }
|
|
end
|
|
if not slab_images[2] then
|
|
slab_images[2] = slab_images[1]
|
|
end
|
|
if not slab_images[3] then
|
|
slab_images[3] = slab_images[2]
|
|
end
|
|
slab_images[4] = "blank.png"
|
|
slab_images[5] = nil
|
|
slab_images[6] = nil
|
|
return slab_images
|
|
end
|
|
local slab_images = parse_images(images)
|
|
local slab_top_images
|
|
if images_top then
|
|
slab_top_images = parse_images(images_top)
|
|
else
|
|
slab_top_images = table.copy(slab_images)
|
|
end
|
|
|
|
local laser_slab_images = table.copy(slab_images)
|
|
laser_slab_images[4] = lzr_laser.LASER_TILE
|
|
|
|
local laser_slab_top_images = table.copy(slab_top_images)
|
|
laser_slab_top_images[4] = lzr_laser.LASER_TILE
|
|
|
|
local new_groups = table.copy(groups)
|
|
new_groups.slab = 1
|
|
new_groups.laser_block = 1
|
|
new_groups.rotatable = 3
|
|
|
|
local def_bottom = {
|
|
description = description,
|
|
drawtype = "mesh",
|
|
__mesh_off = "lzr_stairs_slab_laser.obj",
|
|
__mesh_on = "lzr_stairs_slab_laser.obj",
|
|
__tiles_off = slab_images,
|
|
__tiles_on = laser_slab_images,
|
|
|
|
__use_texture_alpha_off = (src_def and src_def.use_texture_alpha) or "clip",
|
|
__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
|
|
__light_source_on = lzr_globals.LASER_GLOW,
|
|
|
|
paramtype = "light",
|
|
is_ground_content = false,
|
|
groups = new_groups,
|
|
sounds = sounds,
|
|
collision_box = {
|
|
type = "fixed",
|
|
-- The slab nodebox is slightly higher than half of a node
|
|
-- so that an incoming laser from any direction (except the top)
|
|
-- will be visually blocked.
|
|
fixed = {-0.5, -0.5, -0.5, 0.5, 1/16, 0.5},
|
|
},
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-0.5, -0.5, -0.5, 0.5, 1/16, 0.5},
|
|
},
|
|
__on_place = function(itemstack, placer, pointed_thing)
|
|
-- node's on_rightclick action takes precedence
|
|
if pointed_thing.type == "node" and placer then
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
local def = minetest.registered_nodes[node.name]
|
|
local sneak = placer:get_player_control().sneak
|
|
if def and def.on_rightclick and not sneak then
|
|
def.on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing)
|
|
return itemstack
|
|
end
|
|
end
|
|
|
|
local function place_slab_top(itemstack, placer, pointed_thing)
|
|
local itemstack_top = ItemStack(itemstack)
|
|
local iname = itemstack_top:get_name()
|
|
iname = string.gsub(iname, "lzr_stairs:slab_", "lzr_stairs:slab_top_")
|
|
itemstack_top:set_name(iname)
|
|
itemstack_top:set_count(1)
|
|
minetest.item_place(itemstack_top, placer, pointed_thing)
|
|
return itemstack
|
|
end
|
|
|
|
-- Place at wall: Place "top" slab when pointed at upper 9/16th of node side
|
|
if (pointed_thing.above.y == pointed_thing.under.y) then
|
|
local precise = minetest.pointed_thing_to_face_pos(placer, pointed_thing)
|
|
local h = precise.y % 1
|
|
if h < 0.5 and h >= 0.0625 then
|
|
-- Place "top" slab
|
|
itemstack = place_slab_top(itemstack, placer, pointed_thing)
|
|
return itemstack
|
|
else
|
|
-- Normal placement
|
|
itemstack = minetest.item_place(itemstack, placer, pointed_thing)
|
|
return itemstack
|
|
end
|
|
end
|
|
|
|
-- Place at floor or ceiling
|
|
local place_down = pointed_thing.above.y > pointed_thing.under.y
|
|
if place_down then
|
|
itemstack = minetest.item_place(itemstack, placer, pointed_thing)
|
|
else
|
|
itemstack = place_slab_top(itemstack, placer, pointed_thing)
|
|
end
|
|
return itemstack
|
|
end,
|
|
_lzr_on_rotate = function(pos, node, rotate_axis, rotate_dir)
|
|
if rotate_axis ~= "y" then
|
|
node.name = string.gsub(node.name, "lzr_stairs:slab_", "lzr_stairs:slab_top_")
|
|
return true, node
|
|
else
|
|
return false
|
|
end
|
|
end,
|
|
}
|
|
|
|
lzr_laser.register_element(
|
|
":lzr_stairs:slab_" .. subname,
|
|
def_bottom,
|
|
{ group = "laser_element_slab_"..subname }
|
|
)
|
|
|
|
local def_top = table.copy(def_bottom)
|
|
def_top.description = S("@1 (top)", description)
|
|
def_top.__mesh_off = "lzr_stairs_slab_top_laser.obj"
|
|
def_top.__mesh_on = "lzr_stairs_slab_top_laser.obj"
|
|
def_top.__tiles_off = slab_top_images
|
|
def_top.__tiles_on = laser_slab_top_images
|
|
|
|
def_top.groups.slab = 2
|
|
def_top.groups.not_in_creative_inventory = 1
|
|
-- TODO: Drop bottom slab
|
|
def_top.drop = ""
|
|
|
|
def_top.collision_box = {
|
|
type = "fixed",
|
|
-- Same as bottom slab, but shifted to the top
|
|
fixed = { -0.5, -1/16, -0.5, 0.5, 0.5, 0.5 },
|
|
}
|
|
def_top.selection_box = {
|
|
type = "fixed",
|
|
fixed = { -0.5, -1/16, -0.5, 0.5, 0.5, 0.5 },
|
|
}
|
|
def_top.on_place = nil
|
|
def_top._lzr_on_rotate = function(pos, node, rotate_axis, rotate_dir)
|
|
if rotate_axis ~= "y" then
|
|
node.name = string.gsub(node.name, "lzr_stairs:slab_top_", "lzr_stairs:slab_")
|
|
return true, node
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
lzr_laser.register_element(
|
|
":lzr_stairs:slab_top_" .. subname,
|
|
def_top,
|
|
{ group = "laser_element_slab_top_"..subname }
|
|
)
|
|
end
|
|
|
|
-- Register inner stair
|
|
-- Node will be called lzr_stairs:stair_inner_<subname>
|
|
|
|
function lzr_stairs.register_stair_inner(subname, recipeitem, groups, images,
|
|
description, sounds, worldaligntex, full_description, vertical_rotation)
|
|
local src_def = minetest.registered_nodes[recipeitem]
|
|
|
|
-- 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 = 2
|
|
new_groups.rotatable = 3
|
|
if full_description then
|
|
description = full_description
|
|
else
|
|
description = "Inner " .. description
|
|
end
|
|
local paramtype2
|
|
if vertical_rotation == false then
|
|
paramtype2 = "4dir"
|
|
else
|
|
paramtype2 = "facedir"
|
|
end
|
|
minetest.register_node(":lzr_stairs:stair_inner_" .. subname, {
|
|
description = description,
|
|
drawtype = "nodebox",
|
|
tiles = stair_images,
|
|
use_texture_alpha = src_def and src_def.use_texture_alpha,
|
|
paramtype = "light",
|
|
paramtype2 = paramtype2,
|
|
is_ground_content = false,
|
|
groups = new_groups,
|
|
sounds = sounds,
|
|
node_box = {
|
|
-- Unusual stairs nodebox (see stairs comment)
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.5, -0.5, -0.5, 0.5, 1/16, 0.5},
|
|
{-0.5, 1/16, -1/16, 0.5, 0.5, 0.5},
|
|
{-0.5, 1/16, -0.5, 1/16, 0.5, -1/16},
|
|
},
|
|
},
|
|
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,
|
|
})
|
|
|
|
end
|
|
|
|
|
|
-- Register outer stair
|
|
-- Node will be called lzr_stairs:stair_outer_<subname>
|
|
|
|
function lzr_stairs.register_stair_outer(subname, recipeitem, groups, images,
|
|
description, sounds, worldaligntex, full_description, vertical_rotation)
|
|
local src_def = minetest.registered_nodes[recipeitem]
|
|
|
|
-- 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 = 3
|
|
new_groups.rotatable = 3
|
|
if full_description then
|
|
description = full_description
|
|
else
|
|
description = "Outer " .. description
|
|
end
|
|
local paramtype2
|
|
if vertical_rotation == false then
|
|
paramtype2 = "4dir"
|
|
else
|
|
paramtype2 = "facedir"
|
|
end
|
|
minetest.register_node(":lzr_stairs:stair_outer_" .. subname, {
|
|
description = description,
|
|
drawtype = "nodebox",
|
|
tiles = stair_images,
|
|
use_texture_alpha = src_def and src_def.use_texture_alpha,
|
|
paramtype = "light",
|
|
paramtype2 = paramtype2,
|
|
is_ground_content = false,
|
|
groups = new_groups,
|
|
sounds = sounds,
|
|
node_box = {
|
|
-- Unusual stairs nodebox (see stairs comment)
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.5, -0.5, -0.5, 0.5, 1/16, 0.5},
|
|
{-0.5, 1/16, -1/16, 1/16, 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,
|
|
})
|
|
|
|
end
|
|
|
|
|
|
-- Stair/slab registration function.
|
|
-- Nodes will be called lzr_stairs:{stair,slab}_<subname>
|
|
|
|
function lzr_stairs.register_stair_and_slab(subname, recipeitem, groups, images,
|
|
desc_stair, desc_slab, sounds, worldaligntex,
|
|
desc_stair_inner, desc_stair_outer)
|
|
lzr_stairs.register_stair(subname, recipeitem, groups, images, desc_stair,
|
|
sounds, worldaligntex)
|
|
lzr_stairs.register_stair_inner(subname, recipeitem, groups, images,
|
|
desc_stair, sounds, worldaligntex, desc_stair_inner)
|
|
lzr_stairs.register_stair_outer(subname, recipeitem, groups, images,
|
|
desc_stair, sounds, worldaligntex, desc_stair_outer)
|
|
lzr_stairs.register_slab(subname, recipeitem, groups, images, desc_slab,
|
|
sounds, worldaligntex)
|
|
end
|
|
|
|
-- Register default stairs and slabs
|
|
dofile(minetest.get_modpath("lzr_stairs").."/register.lua")
|
|
-- List of node descriptions (only for translation updater scripts)
|
|
dofile(minetest.get_modpath("lzr_stairs").."/translate.lua")
|
|
-- Node aliases for backwards-compatibility
|
|
dofile(minetest.get_modpath("lzr_stairs").."/aliases.lua")
|