Browse Source

Add support for attached facedir/4dir nodes (#11432)

Wuzzy 1 year ago
parent
commit
3c7f26d937

+ 33 - 8
builtin/game/falling.lua

@@ -466,15 +466,39 @@ local function drop_attached_node(p)
 	end
 end
 
-function builtin_shared.check_attached_node(p, n)
+function builtin_shared.check_attached_node(p, n, group_rating)
 	local def = core.registered_nodes[n.name]
 	local d = vector.zero()
-	if def.paramtype2 == "wallmounted" or
+	if group_rating == 3 then
+		-- always attach to floor
+		d.y = -1
+	elseif group_rating == 4 then
+		-- always attach to ceiling
+		d.y = 1
+	elseif group_rating == 2 then
+		-- attach to facedir or 4dir direction
+		if (def.paramtype2 == "facedir" or
+				def.paramtype2 == "colorfacedir") then
+			-- Attach to whatever facedir is "mounted to".
+			-- For facedir, this is where tile no. 5 point at.
+
+			-- The fallback vector here is in case 'facedir to dir' is nil due
+			-- to voxelmanip placing a wallmounted node without resetting a
+			-- pre-existing param2 value that is out-of-range for facedir.
+			-- The fallback vector corresponds to param2 = 0.
+			d = core.facedir_to_dir(n.param2) or vector.new(0, 0, 1)
+		elseif (def.paramtype2 == "4dir" or
+				def.paramtype2 == "color4dir") then
+			-- Similar to facedir handling
+			d = core.fourdir_to_dir(n.param2) or vector.new(0, 0, 1)
+		end
+	elseif def.paramtype2 == "wallmounted" or
 			def.paramtype2 == "colorwallmounted" then
-		-- The fallback vector here is in case 'wallmounted to dir' is nil due
-		-- to voxelmanip placing a wallmounted node without resetting a
-		-- pre-existing param2 value that is out-of-range for wallmounted.
-		-- The fallback vector corresponds to param2 = 0.
+		-- Attach to whatever this node is "mounted to".
+		-- This where tile no. 2 points at.
+
+		-- The fallback vector here is used for the same reason as
+		-- for facedir nodes.
 		d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
 	else
 		d.y = -1
@@ -519,8 +543,9 @@ function core.check_single_for_falling(p)
 		end
 	end
 
-	if core.get_item_group(n.name, "attached_node") ~= 0 then
-		if not builtin_shared.check_attached_node(p, n) then
+	local an = core.get_item_group(n.name, "attached_node")
+	if an ~= 0 then
+		if not builtin_shared.check_attached_node(p, n, an) then
 			drop_attached_node(p)
 			return true
 		end

+ 3 - 2
builtin/game/item.lua

@@ -240,8 +240,9 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
 	end
 
 	-- Check if the node is attached and if it can be placed there
-	if core.get_item_group(def.name, "attached_node") ~= 0 and
-		not builtin_shared.check_attached_node(place_to, newnode) then
+	local an = core.get_item_group(def.name, "attached_node")
+	if an ~= 0 and
+		not builtin_shared.check_attached_node(place_to, newnode, an) then
 		log("action", "attached node " .. def.name ..
 			" cannot be placed at " .. core.pos_to_string(place_to))
 		return itemstack, nil

+ 10 - 3
doc/lua_api.txt

@@ -1890,9 +1890,16 @@ to games.
 
 ### Node-only groups
 
-* `attached_node`: if the node under it is not a walkable block the node will be
-  dropped as an item. If the node is wallmounted the wallmounted direction is
-  checked.
+* `attached_node`: the node is 'attached' to a neighboring node. It checks
+                   whether the node it is attached to is walkable. If it
+                   isn't, the node will drop as an item.
+    * `1`: if the node is wallmounted, the node is attached in the wallmounted
+           direction. Otherwise, the node is attached to the node below.
+    * `2`: if the node is facedir or 4dir, the facedir or 4dir direction is checked.
+           No effect for other nodes.
+           Note: The "attaching face" of this node is tile no. 5 (back face).
+    * `3`: the node is always attached to the node below.
+    * `4`: the node is always attached to the node above.
 * `bouncy`: value is bounce speed in percent.
   If positive, jump/sneak on floor impact will increase/decrease bounce height.
   Negative value is the same bounciness, but non-controllable.

+ 55 - 1
games/devtest/mods/testnodes/properties.lua

@@ -57,7 +57,6 @@ minetest.register_node("testnodes:attached", {
 	},
 	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.
 minetest.register_node("testnodes:attached_wallmounted", {
@@ -73,6 +72,61 @@ minetest.register_node("testnodes:attached_wallmounted", {
 	groups = { attached_node = 1, dig_immediate = 3 },
 })
 
+-- Wallmounted node that always attaches to the floor
+minetest.register_node("testnodes:attached_wallmounted_floor", {
+	description = S("Floor-Attached Wallmounted Node"),
+	paramtype2 = "wallmounted",
+	tiles = {
+		"testnodes_attached_top.png",
+		"testnodes_attached_bottom.png",
+		"testnodes_attached_side.png",
+	},
+	groups = { attached_node = 3, dig_immediate = 3 },
+	color = "#FF8080",
+})
+
+-- This node attaches to the ceiling and drops as item
+-- when the ceiling is gone.
+minetest.register_node("testnodes:attached_top", {
+	description = S("Ceiling-Attached Node"),
+	tiles = {
+		"testnodes_attached_bottom.png",
+		"testnodes_attached_top.png",
+		"testnodes_attached_side.png^[transformR180",
+	},
+	groups = { attached_node = 4, dig_immediate = 3 },
+})
+
+-- Same as wallmounted attached, but for facedir
+minetest.register_node("testnodes:attached_facedir", {
+	description = S("Facedir Attached Node"),
+	paramtype2 = "facedir",
+	tiles = {
+		"testnodes_attachedf_side.png^[transformR180",
+		"testnodes_attachedf_side.png",
+		"testnodes_attachedf_side.png^[transformR90",
+		"testnodes_attachedf_side.png^[transformR270",
+		"testnodes_attachedf_bottom.png",
+		"testnodes_attachedf_top.png",
+	},
+	groups = { attached_node = 2, dig_immediate = 3 },
+})
+
+-- Same as facedir attached, but for 4dir
+minetest.register_node("testnodes:attached_4dir", {
+	description = S("4dir Attached Node"),
+	paramtype2 = "4dir",
+	tiles = {
+		"testnodes_attached4_side.png^[transformR180",
+		"testnodes_attached4_side.png",
+		"testnodes_attached4_side.png^[transformR90",
+		"testnodes_attached4_side.png^[transformR270",
+		"testnodes_attached4_bottom.png",
+		"testnodes_attached4_top.png",
+	},
+	groups = { attached_node = 2, dig_immediate = 3 },
+})
+
 -- Jump disabled
 minetest.register_node("testnodes:nojump", {
 	description = S("Non-jumping Node").."\n"..

BIN
games/devtest/mods/testnodes/textures/testnodes_attached4_bottom.png


BIN
games/devtest/mods/testnodes/textures/testnodes_attached4_side.png


BIN
games/devtest/mods/testnodes/textures/testnodes_attached4_top.png


BIN
games/devtest/mods/testnodes/textures/testnodes_attachedf_bottom.png


BIN
games/devtest/mods/testnodes/textures/testnodes_attachedf_side.png


BIN
games/devtest/mods/testnodes/textures/testnodes_attachedf_top.png


+ 20 - 12
src/client/game.cpp

@@ -3585,22 +3585,30 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
 	}
 
 	// Check attachment if node is in group attached_node
-	if (itemgroup_get(predicted_f.groups, "attached_node") != 0) {
-		const static v3s16 wallmounted_dirs[8] = {
-			v3s16(0, 1, 0),
-			v3s16(0, -1, 0),
-			v3s16(1, 0, 0),
-			v3s16(-1, 0, 0),
-			v3s16(0, 0, 1),
-			v3s16(0, 0, -1),
-		};
+	int an = itemgroup_get(predicted_f.groups, "attached_node");
+	if (an != 0) {
 		v3s16 pp;
 
-		if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
-				predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
+		if (an == 3) {
+			pp = p + v3s16(0, -1, 0);
+		} else if (an == 4) {
+			pp = p + v3s16(0, 1, 0);
+		} else if (an == 2) {
+			if (predicted_f.param_type_2 == CPT2_FACEDIR ||
+					predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
+				pp = p + facedir_dirs[param2];
+			} else if (predicted_f.param_type_2 == CPT2_4DIR ||
+					predicted_f.param_type_2 == CPT2_COLORED_4DIR ) {
+				pp = p + fourdir_dirs[param2];
+			} else {
+				pp = p;
+			}
+		} else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
+				predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
 			pp = p + wallmounted_dirs[param2];
-		else
+		} else {
 			pp = p + v3s16(0, -1, 0);
+		}
 
 		if (!nodedef->get(map.getNode(pp)).walkable) {
 			soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;

+ 49 - 0
src/util/directiontables.cpp

@@ -118,3 +118,52 @@ const u8 wallmounted_to_facedir[6] = {
 	8,
 	4 + 2
 };
+
+const v3s16 wallmounted_dirs[8] = {
+	v3s16(0, 1, 0),
+	v3s16(0, -1, 0),
+	v3s16(1, 0, 0),
+	v3s16(-1, 0, 0),
+	v3s16(0, 0, 1),
+	v3s16(0, 0, -1),
+};
+
+const v3s16 facedir_dirs[32] = {
+	//0
+	v3s16(0, 0, 1),
+	v3s16(1, 0, 0),
+	v3s16(0, 0, -1),
+	v3s16(-1, 0, 0),
+	//4
+	v3s16(0, -1, 0),
+	v3s16(1, 0, 0),
+	v3s16(0, 1, 0),
+	v3s16(-1, 0, 0),
+	//8
+	v3s16(0, 1, 0),
+	v3s16(1, 0, 0),
+	v3s16(0, -1, 0),
+	v3s16(-1, 0, 0),
+	//12
+	v3s16(0, 0, 1),
+	v3s16(0, -1, 0),
+	v3s16(0, 0, -1),
+	v3s16(0, 1, 0),
+	//16
+	v3s16(0, 0, 1),
+	v3s16(0, 1, 0),
+	v3s16(0, 0, -1),
+	v3s16(0, -1, 0),
+	//20
+	v3s16(0, 0, 1),
+	v3s16(-1, 0, 0),
+	v3s16(0, 0, -1),
+	v3s16(1, 0, 0),
+};
+
+const v3s16 fourdir_dirs[4] = {
+	v3s16(0, 0, 1),
+	v3s16(1, 0, 0),
+	v3s16(0, 0, -1),
+	v3s16(-1, 0, 0),
+};

+ 6 - 0
src/util/directiontables.h

@@ -33,6 +33,12 @@ extern const v3s16 g_27dirs[27];
 
 extern const u8 wallmounted_to_facedir[6];
 
+extern const v3s16 wallmounted_dirs[8];
+
+extern const v3s16 facedir_dirs[32];
+
+extern const v3s16 fourdir_dirs[4];
+
 /// Direction in the 6D format. g_27dirs contains corresponding vectors.
 /// Here P means Positive, N stands for Negative.
 enum Direction6D {