Add rotation support for wallmounted nodes in 'ceiling' or 'floor' mode (#11073)

This commit is contained in:
Wuzzy 2024-01-17 17:47:06 +01:00 committed by GitHub
parent e7dd9737bd
commit 08ee6d8d4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 375 additions and 33 deletions

@ -144,6 +144,8 @@ local wallmounted_to_dir = {
vector.new(-1, 0, 0), vector.new(-1, 0, 0),
vector.new( 0, 0, 1), vector.new( 0, 0, 1),
vector.new( 0, 0, -1), vector.new( 0, 0, -1),
vector.new( 0, 1, 0),
vector.new( 0, -1, 0),
} }
function core.wallmounted_to_dir(wallmounted) function core.wallmounted_to_dir(wallmounted)
return wallmounted_to_dir[wallmounted % 8] return wallmounted_to_dir[wallmounted % 8]

@ -150,7 +150,12 @@ core.register_entity(":__builtin:falling_node", {
-- Rotate entity -- Rotate entity
if def.drawtype == "torchlike" then if def.drawtype == "torchlike" then
self.object:set_yaw(math.pi*0.25) if (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted")
and node.param2 % 8 == 7 then
self.object:set_yaw(-math.pi*0.25)
else
self.object:set_yaw(math.pi*0.25)
end
elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh") elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh")
and (def.wield_image == "" or def.wield_image == nil)) and (def.wield_image == "" or def.wield_image == nil))
or def.drawtype == "signlike" or def.drawtype == "signlike"
@ -190,6 +195,10 @@ core.register_entity(":__builtin:falling_node", {
pitch, yaw = 0, -math.pi/2 pitch, yaw = 0, -math.pi/2
elseif rot == 4 then elseif rot == 4 then
pitch, yaw = 0, math.pi pitch, yaw = 0, math.pi
elseif rot == 6 then
pitch, yaw = math.pi/2, 0
elseif rot == 7 then
pitch, yaw = -math.pi/2, math.pi
end end
else else
if rot == 1 then if rot == 1 then
@ -202,6 +211,10 @@ core.register_entity(":__builtin:falling_node", {
pitch, yaw = math.pi/2, math.pi pitch, yaw = math.pi/2, math.pi
elseif rot == 5 then elseif rot == 5 then
pitch, yaw = math.pi/2, 0 pitch, yaw = math.pi/2, 0
elseif rot == 6 then
pitch, yaw = math.pi, -math.pi/2
elseif rot == 7 then
pitch, yaw = 0, -math.pi/2
end end
end end
if def.drawtype == "signlike" then if def.drawtype == "signlike" then
@ -210,10 +223,20 @@ core.register_entity(":__builtin:falling_node", {
yaw = yaw + math.pi/2 yaw = yaw + math.pi/2
elseif rot == 1 then elseif rot == 1 then
yaw = yaw - math.pi/2 yaw = yaw - math.pi/2
elseif rot == 6 then
yaw = yaw - math.pi/2
pitch = pitch + math.pi
elseif rot == 7 then
yaw = yaw + math.pi/2
pitch = pitch + math.pi
end end
elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
if rot >= 0 and rot <= 1 then if rot == 0 or rot == 1 then
roll = roll + math.pi roll = roll + math.pi
elseif rot == 6 or rot == 7 then
if def.drawtype ~= "normal" then
roll = roll - math.pi/2
end
else else
yaw = yaw + math.pi yaw = yaw + math.pi
end end

@ -32,6 +32,7 @@ core.features = {
hud_def_type_field = true, hud_def_type_field = true,
random_state_restore = true, random_state_restore = true,
after_order_expiry_registration = true, after_order_expiry_registration = true,
wallmounted_rotate = true,
} }
function core.has_feature(arg) function core.has_feature(arg)

@ -202,7 +202,40 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
elseif (def.paramtype2 == "wallmounted" or elseif (def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted") and not param2 then def.paramtype2 == "colorwallmounted") and not param2 then
local dir = vector.subtract(under, above) local dir = vector.subtract(under, above)
-- If you change this code, also change src/client/game.cpp
newnode.param2 = core.dir_to_wallmounted(dir) newnode.param2 = core.dir_to_wallmounted(dir)
if def.wallmounted_rotate_vertical and
(newnode.param2 == 0 or newnode.param2 == 1) then
local placer_pos = placer and placer:get_pos()
if placer_pos then
local pdir = {
x = above.x - placer_pos.x,
y = dir.y,
z = above.z - placer_pos.z
}
local rotate = false
if def.drawtype == "torchlike" then
if not ((pdir.x < 0 and pdir.z > 0) or
(pdir.x > 0 and pdir.z < 0)) then
rotate = true
end
if pdir.y > 0 then
rotate = not rotate
end
elseif def.drawtype == "signlike" then
if math.abs(pdir.x) < math.abs(pdir.z) then
rotate = true
end
else
if math.abs(pdir.x) > math.abs(pdir.z) then
rotate = true
end
end
if rotate then
newnode.param2 = newnode.param2 + 6
end
end
end
-- Calculate the direction for furnaces and chests and stuff -- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir" or def.paramtype2 == "colorfacedir" or

@ -1270,11 +1270,15 @@ The function of `param2` is determined by `paramtype2` in node definition.
* The rotation of the node is stored in `param2` * The rotation of the node is stored in `param2`
* Node is 'mounted'/facing towards one of 6 directions * Node is 'mounted'/facing towards one of 6 directions
* You can make this value by using `minetest.dir_to_wallmounted()` * You can make this value by using `minetest.dir_to_wallmounted()`
* Values range 0 - 5 * Values range 0 - 7
* The value denotes at which direction the node is "mounted": * The value denotes at which direction the node is "mounted":
0 = y+, 1 = y-, 2 = x+, 3 = x-, 4 = z+, 5 = z- 0 = y+, 1 = y-, 2 = x+, 3 = x-, 4 = z+, 5 = z-
6 = y+, but rotated by 90°
7 = y-, but rotated by -90°
* By default, on placement the param2 is automatically set to the * By default, on placement the param2 is automatically set to the
appropriate rotation, depending on which side was pointed at appropriate rotation (0 to 5), depending on which side was
pointed at. With the node field `wallmounted_rotate_vertical = true`,
the param2 values 6 and 7 might additionally be set
* `paramtype2 = "facedir"` * `paramtype2 = "facedir"`
* Supported drawtypes: "normal", "nodebox", "mesh" * Supported drawtypes: "normal", "nodebox", "mesh"
* The rotation of the node is stored in `param2`. * The rotation of the node is stored in `param2`.
@ -5291,6 +5295,9 @@ Utilities
-- minetest.after guarantees that coexisting jobs are executed primarily -- minetest.after guarantees that coexisting jobs are executed primarily
-- in order of expiry and secondarily in order of registration (5.9.0) -- in order of expiry and secondarily in order of registration (5.9.0)
after_order_expiry_registration = true, after_order_expiry_registration = true,
-- wallmounted nodes mounted at floor or ceiling may additionally
-- be rotated by 90° with special param2 values (5.9.0)
wallmounted_rotate = true,
} }
``` ```
@ -8926,6 +8933,13 @@ Used by `minetest.register_node`.
place_param2 = 0, place_param2 = 0,
-- Value for param2 that is set when player places node -- Value for param2 that is set when player places node
wallmounted_rotate_vertical = false,
-- If true, place_param2 is nil, and this is a wallmounted node,
-- this node might use the special 90° rotation when placed
-- on the floor or ceiling, depending on the direction.
-- See the explanation about wallmounted for details.
-- Otherwise, the rotation is always the same on vertical placement.
is_ground_content = true, is_ground_content = true,
-- If false, the cave generator and dungeon generator will not carve -- If false, the cave generator and dungeon generator will not carve
-- through this node. -- through this node.

@ -163,7 +163,7 @@ minetest.register_node("testnodes:torchlike", {
minetest.register_node("testnodes:torchlike_wallmounted", { minetest.register_node("testnodes:torchlike_wallmounted", {
description = S("Wallmounted \"torchlike\" Drawtype Test Node").."\n".. description = S("Wallmounted \"torchlike\" Drawtype Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
drawtype = "torchlike", drawtype = "torchlike",
paramtype = "light", paramtype = "light",
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
@ -179,6 +179,24 @@ minetest.register_node("testnodes:torchlike_wallmounted", {
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
minetest.register_node("testnodes:torchlike_wallmounted_rot", {
description = S("Wallmounted Rotatable Torchlike Drawtype Test Node"),
drawtype = "torchlike",
paramtype = "light",
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
tiles = {
"testnodes_torchlike_floor.png^[colorize:#FFFF00:40",
"testnodes_torchlike_ceiling.png^[colorize:#FFFF00:40",
"testnodes_torchlike_wall.png^[colorize:#FFFF00:40",
},
walkable = false,
sunlight_propagates = true,
groups = { dig_immediate = 3 },
})
minetest.register_node("testnodes:signlike", { minetest.register_node("testnodes:signlike", {
description = S("Floor \"signlike\" Drawtype Test Node").."\n".. description = S("Floor \"signlike\" Drawtype Test Node").."\n"..
S("Always on floor"), S("Always on floor"),
@ -186,16 +204,14 @@ minetest.register_node("testnodes:signlike", {
paramtype = "light", paramtype = "light",
tiles = { "testnodes_signlike.png^[colorize:#FF0000:64" }, tiles = { "testnodes_signlike.png^[colorize:#FF0000:64" },
walkable = false, walkable = false,
groups = { dig_immediate = 3 },
sunlight_propagates = true, sunlight_propagates = true,
groups = { dig_immediate = 3 },
}) })
minetest.register_node("testnodes:signlike_wallmounted", { minetest.register_node("testnodes:signlike_wallmounted", {
description = S("Wallmounted \"signlike\" Drawtype Test Node").."\n".. description = S("Wallmounted \"signlike\" Drawtype Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
drawtype = "signlike", drawtype = "signlike",
paramtype = "light", paramtype = "light",
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
@ -207,6 +223,22 @@ minetest.register_node("testnodes:signlike_wallmounted", {
sunlight_propagates = true, sunlight_propagates = true,
}) })
minetest.register_node("testnodes:signlike_rot", {
description = S("Wallmounted Rotatable Signlike Drawtype Test Node"),
drawtype = "signlike",
paramtype = "light",
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
tiles = { "testnodes_signlike.png^[colorize:#FFFF00:40" },
walkable = false,
groups = { dig_immediate = 3 },
sunlight_propagates = true,
})
minetest.register_node("testnodes:plantlike", { minetest.register_node("testnodes:plantlike", {
description = S("\"plantlike\" Drawtype Test Node"), description = S("\"plantlike\" Drawtype Test Node"),
drawtype = "plantlike", drawtype = "plantlike",
@ -235,7 +267,7 @@ minetest.register_node("testnodes:plantlike_waving", {
minetest.register_node("testnodes:plantlike_wallmounted", { minetest.register_node("testnodes:plantlike_wallmounted", {
description = S("Wallmounted \"plantlike\" Drawtype Test Node").."\n".. description = S("Wallmounted \"plantlike\" Drawtype Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
drawtype = "plantlike", drawtype = "plantlike",
paramtype = "light", paramtype = "light",
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
@ -366,7 +398,7 @@ minetest.register_node("testnodes:plantlike_rooted", {
minetest.register_node("testnodes:plantlike_rooted_wallmounted", { minetest.register_node("testnodes:plantlike_rooted_wallmounted", {
description = S("Wallmounted \"rooted_plantlike\" Drawtype Test Node").."\n".. description = S("Wallmounted \"rooted_plantlike\" Drawtype Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
drawtype = "plantlike_rooted", drawtype = "plantlike_rooted",
paramtype = "light", paramtype = "light",
paramtype2 = "wallmounted", paramtype2 = "wallmounted",

@ -92,7 +92,7 @@ minetest.register_node("testnodes:mesh_color4dir", {
-- Wallmounted mesh: pyramid -- Wallmounted mesh: pyramid
minetest.register_node("testnodes:mesh_wallmounted", { minetest.register_node("testnodes:mesh_wallmounted", {
description = S("Wallmounted Mesh Test Node").."\n".. description = S("Wallmounted Mesh Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
drawtype = "mesh", drawtype = "mesh",
mesh = "testnodes_pyramid.obj", mesh = "testnodes_pyramid.obj",
tiles = {"testnodes_mesh_stripes9.png"}, tiles = {"testnodes_mesh_stripes9.png"},
@ -105,7 +105,7 @@ minetest.register_node("testnodes:mesh_wallmounted", {
minetest.register_node("testnodes:mesh_colorwallmounted", { minetest.register_node("testnodes:mesh_colorwallmounted", {
description = S("Color Wallmounted Mesh Test Node").."\n".. description = S("Color Wallmounted Mesh Test Node").."\n"..
S("param2 = color + wallmounted rotation (0..5, 8..13, ...)"), S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"),
drawtype = "mesh", drawtype = "mesh",
mesh = "testnodes_pyramid.obj", mesh = "testnodes_pyramid.obj",
tiles = {"testnodes_mesh_stripes10.png"}, tiles = {"testnodes_mesh_stripes10.png"},

@ -180,3 +180,63 @@ minetest.register_node("testnodes:facedir_to_connect_to", {
paramtype2 = "facedir", paramtype2 = "facedir",
connect_sides = {"left", "top"}, connect_sides = {"left", "top"},
}) })
-- 3D sign and button:
-- These are example nodes for more realistic example uses
-- of wallmounted_rotate_vertical
minetest.register_node("testnodes:sign3d", {
description = S("Nodebox Sign, Nodebox Type \"fixed\""),
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
sunlight_propagates = true,
walkable = false,
tiles = {
"testnodes_sign3d.png",
},
groups = { dig_immediate = 3 },
node_box = {
type = "fixed",
fixed = {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125},
},
})
minetest.register_node("testnodes:sign3d_wallmounted", {
description = S("Nodebox Sign, Nodebox Type \"wallmounted\""),
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
sunlight_propagates = true,
walkable = false,
tiles = {
"testnodes_sign3d.png^[colorize:#ff0000:127",
},
groups = { dig_immediate = 3 },
node_box = {
type = "wallmounted",
wall_top = {-0.4375, 0.4375, -0.3125, 0.4375, 0.5, 0.3125},
wall_bottom = {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125},
wall_side = {-0.5, -0.3125, -0.4375, -0.4375, 0.3125, 0.4375},
},
})
minetest.register_node("testnodes:button", {
description = S("Button Nodebox Test Node"),
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
sunlight_propagates = true,
walkable = false,
tiles = {
"testnodes_nodebox.png",
},
groups = { dig_immediate = 3 },
node_box = {
type = "fixed",
fixed = { -4/16, -8/16, -2/16, 4/16, -6/16, 2/16 },
},
})

@ -80,7 +80,7 @@ minetest.register_node("testnodes:4dir_nodebox", {
minetest.register_node("testnodes:wallmounted", { minetest.register_node("testnodes:wallmounted", {
description = S("Wallmounted Test Node").."\n".. description = S("Wallmounted Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
tiles = { tiles = {
"testnodes_1w.png", "testnodes_1w.png",
@ -94,9 +94,25 @@ minetest.register_node("testnodes:wallmounted", {
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
minetest.register_node("testnodes:wallmounted_rot", {
description = S("Wallmounted Rotatable Test Node"),
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
tiles = {
"testnodes_1w.png^[colorize:#FFFF00:40",
"testnodes_2w.png^[colorize:#FFFF00:40",
"testnodes_3w.png^[colorize:#FFFF00:40",
"testnodes_4w.png^[colorize:#FFFF00:40",
"testnodes_5w.png^[colorize:#FFFF00:40",
"testnodes_6w.png^[colorize:#FFFF00:40",
},
groups = { dig_immediate = 3 },
})
minetest.register_node("testnodes:wallmounted_nodebox", { minetest.register_node("testnodes:wallmounted_nodebox", {
description = S("Wallmounted Nodebox Test Node").."\n".. description = S("Wallmounted Nodebox Test Node").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
paramtype = "light", paramtype = "light",
tiles = { tiles = {
@ -118,6 +134,30 @@ minetest.register_node("testnodes:wallmounted_nodebox", {
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
minetest.register_node("testnodes:wallmounted_nodebox_rot", {
description = S("Wallmounted Rotatable Nodebox Test Node"),
paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
paramtype = "light",
tiles = {
"testnodes_1w.png^[colorize:#FFFF00:40",
"testnodes_2w.png^[colorize:#FFFF00:40",
"testnodes_3w.png^[colorize:#FFFF00:40",
"testnodes_4w.png^[colorize:#FFFF00:40",
"testnodes_5w.png^[colorize:#FFFF00:40",
"testnodes_6w.png^[colorize:#FFFF00:40",
},
drawtype = "nodebox",
node_box = {
type = "wallmounted",
wall_top = { -0.5, 0, -0.5, 0.5, 0.5, 0.5 },
wall_bottom = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 },
wall_side = { -0.5, -0.5, -0.5, 0, 0.5, 0.5 },
},
groups = { dig_immediate = 3 },
})
minetest.register_node("testnodes:color", { minetest.register_node("testnodes:color", {
description = S("Color Test Node").."\n".. description = S("Color Test Node").."\n"..
S("param2 = color (0..255)"), S("param2 = color (0..255)"),
@ -212,7 +252,7 @@ minetest.register_node("testnodes:color4dir_nodebox", {
minetest.register_node("testnodes:colorwallmounted", { minetest.register_node("testnodes:colorwallmounted", {
description = S("Color Wallmounted Test Node").."\n".. description = S("Color Wallmounted Test Node").."\n"..
S("param2 = color + wallmounted rotation (0..5, 8..13, ...)"), S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"),
paramtype2 = "colorwallmounted", paramtype2 = "colorwallmounted",
paramtype = "light", paramtype = "light",
palette = "testnodes_palette_wallmounted.png", palette = "testnodes_palette_wallmounted.png",
@ -230,7 +270,7 @@ minetest.register_node("testnodes:colorwallmounted", {
minetest.register_node("testnodes:colorwallmounted_nodebox", { minetest.register_node("testnodes:colorwallmounted_nodebox", {
description = S("Color Wallmounted Nodebox Test Node").."\n".. description = S("Color Wallmounted Nodebox Test Node").."\n"..
S("param2 = color + wallmounted rotation (0..5, 8..13, ...)"), S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"),
paramtype2 = "colorwallmounted", paramtype2 = "colorwallmounted",
paramtype = "light", paramtype = "light",
palette = "testnodes_palette_wallmounted.png", palette = "testnodes_palette_wallmounted.png",

@ -61,8 +61,8 @@ minetest.register_node("testnodes:attached", {
-- when the node it attaches to is gone. -- when the node it attaches to is gone.
minetest.register_node("testnodes:attached_wallmounted", { minetest.register_node("testnodes:attached_wallmounted", {
description = S("Wallmounted Attached Node").."\n".. description = S("Wallmounted Attached Node").."\n"..
S("Attaches to wall; drops as item if neighbor node is gone").."\n".. S("Attaches to solid node it was placed on; drops as item if neighbor node is gone").."\n"..
S("param2 = wallmounted rotation (0..5)"), S("param2 = wallmounted rotation (0..7)"),
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
tiles = { tiles = {
"testnodes_attachedw_top.png", "testnodes_attachedw_top.png",
@ -72,9 +72,29 @@ minetest.register_node("testnodes:attached_wallmounted", {
groups = { attached_node = 1, dig_immediate = 3 }, groups = { attached_node = 1, dig_immediate = 3 },
}) })
-- This node attaches to the side of a node and drops as item
-- when the node it attaches to is gone.
-- Also adds vertical 90° rotation variants.
minetest.register_node("testnodes:attached_wallmounted_rot", {
description = S("Rotatable Wallmounted Attached Node").."\n"..
S("Attaches to solid node it was placed on; drops as item if neighbor node is gone").."\n"..
S("param2 = wallmounted rotation (0..7)").."\n"..
S("May be rotated by 90° if placed at floor or ceiling"),
paramtype2 = "wallmounted",
tiles = {
"testnodes_attachedwr_top.png",
"testnodes_attachedwr_bottom.png",
"testnodes_attachedwr_side.png",
},
wallmounted_rotate_vertical = true,
groups = { attached_node = 1, dig_immediate = 3 },
})
-- Wallmounted node that always attaches to the floor -- Wallmounted node that always attaches to the floor
minetest.register_node("testnodes:attached_wallmounted_floor", { minetest.register_node("testnodes:attached_wallmounted_floor", {
description = S("Floor-Attached Wallmounted Node"), description = S("Floor-Attached Wallmounted Node").."\n"..
S("Drops as item if no solid node below (regardless of rotation)").."\n"..
S("param2 = wallmounted rotation (visual only) (0..7)"),
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
tiles = { tiles = {
"testnodes_attached_top.png", "testnodes_attached_top.png",
@ -85,10 +105,28 @@ minetest.register_node("testnodes:attached_wallmounted_floor", {
color = "#FF8080", color = "#FF8080",
}) })
-- Wallmounted node that always attaches to the floor.
-- Also adds 90° rotation variants.
minetest.register_node("testnodes:attached_wallmounted_floor_rot", {
description = S("Rotatable Floor-Attached Wallmounted Node").."\n"..
S("Drops as item if no solid node below (regardless of rotation)").."\n"..
S("param2 = wallmounted rotation (visual only) (0..7)").."\n"..
S("May be rotated by 90° if placed at floor or ceiling"),
paramtype2 = "wallmounted",
tiles = {
"testnodes_attachedfr_top.png",
"testnodes_attachedfr_bottom.png",
"testnodes_attachedfr_side.png",
},
wallmounted_rotate_vertical = true,
groups = { attached_node = 3, dig_immediate = 3 },
})
-- This node attaches to the ceiling and drops as item -- This node attaches to the ceiling and drops as item
-- when the ceiling is gone. -- when the ceiling is gone.
minetest.register_node("testnodes:attached_top", { minetest.register_node("testnodes:attached_top", {
description = S("Ceiling-Attached Node"), description = S("Ceiling-Attached Node").."\n"..
S("Drops as item if no solid node above"),
tiles = { tiles = {
"testnodes_attached_bottom.png", "testnodes_attached_bottom.png",
"testnodes_attached_top.png", "testnodes_attached_top.png",
@ -99,7 +137,9 @@ minetest.register_node("testnodes:attached_top", {
-- Same as wallmounted attached, but for facedir -- Same as wallmounted attached, but for facedir
minetest.register_node("testnodes:attached_facedir", { minetest.register_node("testnodes:attached_facedir", {
description = S("Facedir Attached Node"), description = S("Facedir Attached Node").."\n"..
S("Attaches to a neighboring solid node; drops as item if that node is gone").."\n"..
S("param2 = facedir rotation (0..23)"),
paramtype2 = "facedir", paramtype2 = "facedir",
tiles = { tiles = {
"testnodes_attachedf_side.png^[transformR180", "testnodes_attachedf_side.png^[transformR180",
@ -114,7 +154,9 @@ minetest.register_node("testnodes:attached_facedir", {
-- Same as facedir attached, but for 4dir -- Same as facedir attached, but for 4dir
minetest.register_node("testnodes:attached_4dir", { minetest.register_node("testnodes:attached_4dir", {
description = S("4dir Attached Node"), description = S("4dir Attached Node").."\n"..
S("Attaches to the side of a solid node; drops as item if that node is gone").."\n"..
S("param2 = 4dir rotation (0..3)"),
paramtype2 = "4dir", paramtype2 = "4dir",
tiles = { tiles = {
"testnodes_attached4_side.png^[transformR180", "testnodes_attached4_side.png^[transformR180",

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

@ -1008,7 +1008,9 @@ void MapblockMeshGenerator::drawTorchlikeNode()
switch (wall) { switch (wall) {
case DWM_YP: tileindex = 1; break; // ceiling case DWM_YP: tileindex = 1; break; // ceiling
case DWM_YN: tileindex = 0; break; // floor case DWM_YN: tileindex = 0; break; // floor
default: tileindex = 2; // side (or invalid—should we care?) case DWM_S1: tileindex = 1; break; // ceiling, but rotated
case DWM_S2: tileindex = 0; break; // floor, but rotated
default: tileindex = 2; // side (or invalid, shouldn't happen)
} }
useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
@ -1044,6 +1046,17 @@ void MapblockMeshGenerator::drawTorchlikeNode()
case DWM_ZN: case DWM_ZN:
vertex.X += -size + BS/2; vertex.X += -size + BS/2;
vertex.rotateXZBy(-90); vertex.rotateXZBy(-90);
break;
case DWM_S1:
// same as DWM_YP, but rotated 90°
vertex.Y += -size + BS/2;
vertex.rotateXZBy(45);
break;
case DWM_S2:
// same as DWM_YN, but rotated -90°
vertex.Y += size - BS/2;
vertex.rotateXZBy(-45);
break;
} }
} }
drawQuad(vertices); drawQuad(vertices);
@ -1077,6 +1090,10 @@ void MapblockMeshGenerator::drawSignlikeNode()
vertex.rotateXZBy( 90); break; vertex.rotateXZBy( 90); break;
case DWM_ZN: case DWM_ZN:
vertex.rotateXZBy(-90); break; vertex.rotateXZBy(-90); break;
case DWM_S1:
vertex.rotateXYBy( 90); vertex.rotateXZBy(90); break;
case DWM_S2:
vertex.rotateXYBy(-90); vertex.rotateXZBy(-90); break;
} }
} }
drawQuad(vertices); drawQuad(vertices);

@ -3704,7 +3704,36 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
v3s16 dir = nodepos - neighborpos; v3s16 dir = nodepos - neighborpos;
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
predicted_node.setParam2(dir.Y < 0 ? 1 : 0); // If you change this code, also change builtin/game/item.lua
u8 predicted_param2 = dir.Y < 0 ? 1 : 0;
if (selected_def.wallmounted_rotate_vertical) {
bool rotate90 = false;
v3f fnodepos = v3f(neighborpos.X, neighborpos.Y, neighborpos.Z);
v3f ppos = client->getEnv().getLocalPlayer()->getPosition() / BS;
v3f pdir = fnodepos - ppos;
switch (predicted_f.drawtype) {
case NDT_TORCHLIKE: {
rotate90 = !((pdir.X < 0 && pdir.Z > 0) ||
(pdir.X > 0 && pdir.Z < 0));
if (dir.Y > 0) {
rotate90 = !rotate90;
}
break;
};
case NDT_SIGNLIKE: {
rotate90 = abs(pdir.X) < abs(pdir.Z);
break;
}
default: {
rotate90 = abs(pdir.X) > abs(pdir.Z);
break;
}
}
if (rotate90) {
predicted_param2 += 6;
}
}
predicted_node.setParam2(predicted_param2);
} else if (abs(dir.X) > abs(dir.Z)) { } else if (abs(dir.X) > abs(dir.Z)) {
predicted_node.setParam2(dir.X < 0 ? 3 : 2); predicted_node.setParam2(dir.X < 0 ? 3 : 2);
} else { } else {

@ -76,6 +76,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
groups = def.groups; groups = def.groups;
node_placement_prediction = def.node_placement_prediction; node_placement_prediction = def.node_placement_prediction;
place_param2 = def.place_param2; place_param2 = def.place_param2;
wallmounted_rotate_vertical = def.wallmounted_rotate_vertical;
sound_place = def.sound_place; sound_place = def.sound_place;
sound_place_failed = def.sound_place_failed; sound_place_failed = def.sound_place_failed;
sound_use = def.sound_use; sound_use = def.sound_use;
@ -124,6 +125,7 @@ void ItemDefinition::reset()
range = -1; range = -1;
node_placement_prediction.clear(); node_placement_prediction.clear();
place_param2.reset(); place_param2.reset();
wallmounted_rotate_vertical = false;
} }
void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
@ -183,6 +185,7 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
os << (u8)place_param2.has_value(); // protocol_version >= 43 os << (u8)place_param2.has_value(); // protocol_version >= 43
if (place_param2) if (place_param2)
os << *place_param2; os << *place_param2;
writeU8(os, wallmounted_rotate_vertical);
} }
void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version) void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
@ -251,6 +254,8 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
if (readU8(is)) // protocol_version >= 43 if (readU8(is)) // protocol_version >= 43
place_param2 = readU8(is); place_param2 = readU8(is);
wallmounted_rotate_vertical = readU8(is); // 0 if missing
} catch(SerializationError &e) {}; } catch(SerializationError &e) {};
} }

@ -89,6 +89,7 @@ struct ItemDefinition
// "" = no prediction // "" = no prediction
std::string node_placement_prediction; std::string node_placement_prediction;
std::optional<u8> place_param2; std::optional<u8> place_param2;
bool wallmounted_rotate_vertical;
/* /*
Some helpful methods Some helpful methods

@ -95,6 +95,8 @@ v3s16 MapNode::getWallMountedDir(const NodeDefManager *nodemgr) const
case 3: return v3s16(-1,0,0); case 3: return v3s16(-1,0,0);
case 4: return v3s16(0,0,1); case 4: return v3s16(0,0,1);
case 5: return v3s16(0,0,-1); case 5: return v3s16(0,0,-1);
case 6: return v3s16(0,1,0);
case 7: return v3s16(0,-1,0);
} }
} }
@ -323,16 +325,45 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
else if(nodebox.type == NODEBOX_WALLMOUNTED) else if(nodebox.type == NODEBOX_WALLMOUNTED)
{ {
v3s16 dir = n.getWallMountedDir(nodemgr); v3s16 dir = n.getWallMountedDir(nodemgr);
u8 wall = n.getWallMounted(nodemgr);
// top // top
if(dir == v3s16(0,1,0)) if(dir == v3s16(0,1,0))
{ {
boxes.push_back(nodebox.wall_top); if (wall == DWM_S1) {
v3f vertices[2] =
{
nodebox.wall_top.MinEdge,
nodebox.wall_top.MaxEdge
};
for (v3f &vertex : vertices) {
vertex.rotateXZBy(90);
}
aabb3f box = aabb3f(vertices[0]);
box.addInternalPoint(vertices[1]);
boxes.push_back(box);
} else {
boxes.push_back(nodebox.wall_top);
}
} }
// bottom // bottom
else if(dir == v3s16(0,-1,0)) else if(dir == v3s16(0,-1,0))
{ {
boxes.push_back(nodebox.wall_bottom); if (wall == DWM_S2) {
v3f vertices[2] =
{
nodebox.wall_bottom.MinEdge,
nodebox.wall_bottom.MaxEdge
};
for (v3f &vertex : vertices) {
vertex.rotateXZBy(-90);
}
aabb3f box = aabb3f(vertices[0]);
box.addInternalPoint(vertices[1]);
boxes.push_back(box);
} else {
boxes.push_back(nodebox.wall_bottom);
}
} }
// side // side
else else

@ -136,6 +136,8 @@ void read_item_definition(lua_State* L, int index,
int place_param2; int place_param2;
if (getintfield(L, index, "place_param2", place_param2)) if (getintfield(L, index, "place_param2", place_param2))
def.place_param2 = rangelim(place_param2, 0, U8_MAX); def.place_param2 = rangelim(place_param2, 0, U8_MAX);
getboolfield(L, index, "wallmounted_rotate_vertical", def.wallmounted_rotate_vertical);
} }
/******************************************************************************/ /******************************************************************************/
@ -195,6 +197,8 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
lua_setfield(L, -2, "sound_place_failed"); lua_setfield(L, -2, "sound_place_failed");
lua_pushstring(L, i.node_placement_prediction.c_str()); lua_pushstring(L, i.node_placement_prediction.c_str());
lua_setfield(L, -2, "node_placement_prediction"); lua_setfield(L, -2, "node_placement_prediction");
lua_pushboolean(L, i.wallmounted_rotate_vertical);
lua_setfield(L, -2, "wallmounted_rotate_vertical");
} }
/******************************************************************************/ /******************************************************************************/

@ -110,13 +110,15 @@ const v3s16 g_27dirs[27] =
v3s16(0,0,0), v3s16(0,0,0),
}; };
const u8 wallmounted_to_facedir[6] = { const u8 wallmounted_to_facedir[8] = {
20, 20,
0, 0,
16 + 1, 16 + 1,
12 + 3, 12 + 3,
8, 8,
4 + 2 4 + 2,
20 + 1, // special 1
0 + 1 // special 2
}; };
const v3s16 wallmounted_dirs[8] = { const v3s16 wallmounted_dirs[8] = {

@ -69,16 +69,22 @@ enum Direction6D {
/// Direction in the wallmounted format. /// Direction in the wallmounted format.
/// P is Positive, N is Negative. /// P is Positive, N is Negative.
enum DirectionWallmounted { enum DirectionWallmounted {
// The 6 wallmounted directions
DWM_YP, DWM_YP,
DWM_YN, DWM_YN,
DWM_XP, DWM_XP,
DWM_XN, DWM_XN,
DWM_ZP, DWM_ZP,
DWM_ZN, DWM_ZN,
DWM_COUNT, // There are 6 wallmounted directions, but 8 possible states (3 bits).
// So we have 2 additional states, which drawtypes might use for
// special ("S") behavior.
DWM_S1,
DWM_S2,
DWM_COUNT
}; };
extern const v3s16 g_6dirs[DWM_COUNT]; extern const v3s16 g_6dirs[6];
extern const v3s16 g_7dirs[7]; extern const v3s16 g_7dirs[7];
@ -87,9 +93,9 @@ extern const v3s16 g_26dirs[26];
// 26th is (0,0,0) // 26th is (0,0,0)
extern const v3s16 g_27dirs[27]; extern const v3s16 g_27dirs[27];
extern const u8 wallmounted_to_facedir[6]; extern const u8 wallmounted_to_facedir[DWM_COUNT];
extern const v3s16 wallmounted_dirs[8]; extern const v3s16 wallmounted_dirs[DWM_COUNT];
extern const v3s16 facedir_dirs[32]; extern const v3s16 facedir_dirs[32];