Allow nodes to specify which sides to connect to.
authorAuke Kok <sofar@foo-projects.org>
Fri, 4 Mar 2016 07:18:04 +0000 (23:18 -0800)
committerShadowNinja <shadowninja@minetest.net>
Sat, 12 Mar 2016 17:08:17 +0000 (12:08 -0500)
NDT_CONNECTED attempts to connect to any side of nodes that it can
connect to, which is troublesome for FACEDIR type nodes that generally
may only have one usable face, and can be rotated.

We introduce a node parameter `connect_sides` that is valid for
any node type. If specified, it lists faces of the node (in "top",
"bottom", "front", "left", "back", "right", form, as array) that
connecting nodeboxes can connect to. "front" corresponds to the south
facing side of a node with facedir = 0.

If the node is rotatable using *simple* FACEDIR, then the attached
face is properly rotated before checking. This allows e.g. a chest
to be attached to only from the rear side.

doc/lua_api.txt
src/collision.cpp
src/content_mapblock.cpp
src/nodedef.cpp
src/nodedef.h
src/script/common/c_content.cpp

index f51c950c3d9c5a341ae7b1cc0fb9c5c4ad9fde81..733ac8412213d6e868862118fb7e06fef4a0ac47 100644 (file)
@@ -3477,6 +3477,8 @@ Definition tables
         * Used for nodebox nodes with the type == "connected"
         * Specifies to what neighboring nodes connections will be drawn
         * e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]]
+        connect_sides = { "top", "bottom", "front", "left", "back", "right" }, --[[
+        ^ Tells connected nodebox nodes to connect only to these sides of this node. ]]
         mesh = "model",
         selection_box = {type="regular"}, -- See "Node boxes" --[[
         ^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]]
index a3979f1dc03289a70f1e8dd755fc128ca12769bb..16db3310c0d5b9640aa60c57b32abdf206382dd4 100644 (file)
@@ -189,7 +189,7 @@ static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
                Map *map, MapNode n, int v, int *neighbors)
 {
        MapNode n2 = map->getNodeNoEx(p);
-       if (nodedef->nodeboxConnects(n, n2))
+       if (nodedef->nodeboxConnects(n, n2, v))
                *neighbors |= v;
 }
 
index c2934f26a0b7985d00044887450d184c7ae34f53..6a83bd8f3821b47c260fd052ffd58f5348dccbbd 100644 (file)
@@ -167,7 +167,7 @@ static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
                MeshMakeData *data, MapNode n, int v, int *neighbors)
 {
        MapNode n2 = data->m_vmanip.getNodeNoEx(p);
-       if (nodedef->nodeboxConnects(n, n2))
+       if (nodedef->nodeboxConnects(n, n2, v))
                *neighbors |= v;
 }
 
index 85cd848aea738852e32143c9ca72dee18cee6ed0..edd02d9f37bbc1f8bcd319ebfde85b37cbfd504e 100644 (file)
@@ -331,6 +331,7 @@ void ContentFeatures::reset()
        sound_dug = SimpleSoundSpec();
        connects_to.clear();
        connects_to_ids.clear();
+       connect_sides = 0;
 }
 
 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
@@ -402,6 +403,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
        for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
                        i != connects_to_ids.end(); ++i)
                writeU16(os, *i);
+       writeU8(os, connect_sides);
 }
 
 void ContentFeatures::deSerialize(std::istream &is)
@@ -479,6 +481,7 @@ void ContentFeatures::deSerialize(std::istream &is)
        u16 connects_to_size = readU16(is);
        for (u16 i = 0; i < connects_to_size; i++)
                connects_to_ids.insert(readU16(is));
+       connect_sides = readU8(is);
        }catch(SerializationError &e) {};
 }
 
@@ -517,7 +520,7 @@ public:
        virtual void runNodeResolveCallbacks();
        virtual void resetNodeResolveState();
        virtual void mapNodeboxConnections();
-       virtual bool nodeboxConnects(MapNode from, MapNode to);
+       virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
 
 private:
        void addNameIdMapping(content_t i, std::string name);
@@ -1530,7 +1533,7 @@ void CNodeDefManager::mapNodeboxConnections()
        }
 }
 
-bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to)
+bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
 {
        const ContentFeatures &f1 = get(from);
 
@@ -1547,6 +1550,24 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to)
                // ignores actually looking if back connection exists
                return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
 
+       // does to node declare usable faces?
+       if (f2.connect_sides > 0) {
+               if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
+                       static const u8 rot[33 * 4] = {
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
+                               8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               32, 16, 8, 4 // 32 - left
+                       };
+                       return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
+               }
+               return (f2.connect_sides & connect_face);
+       }
        // the target is just a regular node, so connect no matter back connection
        return true;
 }
index f92a3a941eca735a5d7b503b6dde8054d52059ba..58d0faffa97e387cb78fce45a0b9d2114c701dd4 100644 (file)
@@ -271,6 +271,8 @@ struct ContentFeatures
        bool legacy_facedir_simple;
        // Set to true if wall_mounted used to be set to true
        bool legacy_wallmounted;
+       // for NDT_CONNECTED pairing
+       u8 connect_sides;
 
        // Sound properties
        SimpleSoundSpec sound_footstep;
@@ -325,7 +327,7 @@ public:
 
        virtual void pendNodeResolve(NodeResolver *nr)=0;
        virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
-       virtual bool nodeboxConnects(const MapNode from, const MapNode to)=0;
+       virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0;
 };
 
 class IWritableNodeDefManager : public INodeDefManager {
index ababf0718ebab51f1f61a346019266e555c56c56..06e20c2a0bd42c1c5922c71bb2b4260a0a940d27 100644 (file)
@@ -547,6 +547,34 @@ ContentFeatures read_content_features(lua_State *L, int index)
        }
        lua_pop(L, 1);
 
+       lua_getfield(L, index, "connect_sides");
+       if (lua_istable(L, -1)) {
+               int table = lua_gettop(L);
+               lua_pushnil(L);
+               while (lua_next(L, table) != 0) {
+                       // Value at -1
+                       std::string side(lua_tostring(L, -1));
+                       // Note faces are flipped to make checking easier
+                       if (side == "top")
+                               f.connect_sides |= 2;
+                       else if (side == "bottom")
+                               f.connect_sides |= 1;
+                       else if (side == "front")
+                               f.connect_sides |= 16;
+                       else if (side == "left")
+                               f.connect_sides |= 32;
+                       else if (side == "back")
+                               f.connect_sides |= 4;
+                       else if (side == "right")
+                               f.connect_sides |= 8;
+                       else
+                               warningstream << "Unknown value for \"connect_sides\": "
+                                       << side << std::endl;
+                       lua_pop(L, 1);
+               }
+       }
+       lua_pop(L, 1);
+
        lua_getfield(L, index, "selection_box");
        if(lua_istable(L, -1))
                f.selection_box = read_nodebox(L, -1);