2024-10-25 13:46:47 +02:00

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")