Rework functionality of leveled nodes (#9852)
authorWuzzy <wuzzy2@mail.ru>
Tue, 19 May 2020 19:08:37 +0000 (21:08 +0200)
committerGitHub <noreply@github.com>
Tue, 19 May 2020 19:08:37 +0000 (21:08 +0200)
Co-authored-by: sfan5 <sfan5@live.de>
Co-authored-by: SmallJoker <SmallJoker@users.noreply.github.com>
builtin/game/falling.lua
doc/lua_api.txt
src/mapnode.cpp
src/mapnode.h
src/nodedef.cpp
src/nodedef.h
src/script/common/c_content.cpp
src/script/lua_api/l_env.cpp

index 7037ae8853b14f4e6e8b167c8e93034961442c63..cdbb13accab0189e25d89c469b5049d577da3d27 100644 (file)
@@ -109,6 +109,7 @@ core.register_entity(":__builtin:falling_node", {
                        if core.is_colored_paramtype(def.paramtype2) then
                                itemstring = core.itemstring_with_palette(itemstring, node.param2)
                        end
+                       -- FIXME: solution needed for paramtype2 == "leveled"
                        local vsize
                        if def.visual_scale then
                                local s = def.visual_scale * SCALE
@@ -122,6 +123,24 @@ core.register_entity(":__builtin:falling_node", {
                        })
                end
 
+               -- Set collision box (certain nodeboxes only for now)
+               local nb_types = {fixed=true, leveled=true, connected=true}
+               if def.drawtype == "nodebox" and def.node_box and
+                       nb_types[def.node_box.type] then
+                       local box = table.copy(def.node_box.fixed)
+                       if type(box[1]) == "table" then
+                               box = #box == 1 and box[1] or nil -- We can only use a single box
+                       end
+                       if box then
+                               if def.paramtype2 == "leveled" and (self.node.level or 0) > 0 then
+                                       box[5] = -0.5 + self.node.level / 64
+                               end
+                               self.object:set_properties({
+                                       collisionbox = box
+                               })
+                       end
+               end
+
                -- Rotate entity
                if def.drawtype == "torchlike" then
                        self.object:set_yaw(math.pi*0.25)
@@ -196,13 +215,16 @@ core.register_entity(":__builtin:falling_node", {
        try_place = function(self, bcp, bcn)
                local bcd = core.registered_nodes[bcn.name]
                -- Add levels if dropped on same leveled node
-               if bcd and bcd.leveled and
+               if bcd and bcd.paramtype2 == "leveled" and
                                bcn.name == self.node.name then
                        local addlevel = self.node.level
-                       if not addlevel or addlevel <= 0 then
+                       if (addlevel or 0) <= 0 then
                                addlevel = bcd.leveled
                        end
-                       if core.add_node_level(bcp, addlevel) == 0 then
+                       if core.add_node_level(bcp, addlevel) < addlevel then
+                               return true
+                       elseif bcd.buildable_to then
+                               -- Node level has already reached max, don't place anything
                                return true
                        end
                end
@@ -351,6 +373,7 @@ local function convert_to_falling_node(pos, node)
        if not obj then
                return false
        end
+       -- remember node level, the entities' set_node() uses this
        node.level = core.get_node_level(pos)
        local meta = core.get_meta(pos)
        local metatable = meta and meta:to_table() or {}
@@ -436,18 +459,23 @@ function core.check_single_for_falling(p)
                -- Only spawn falling node if node below is loaded
                local n_bottom = core.get_node_or_nil(p_bottom)
                local d_bottom = n_bottom and core.registered_nodes[n_bottom.name]
-               if d_bottom and
-
-                               (core.get_item_group(n.name, "float") == 0 or
-                               d_bottom.liquidtype == "none") and
-
-                               (n.name ~= n_bottom.name or (d_bottom.leveled and
-                               core.get_node_level(p_bottom) <
-                               core.get_node_max_level(p_bottom))) and
-
-                               (not d_bottom.walkable or d_bottom.buildable_to) then
-                       convert_to_falling_node(p, n)
-                       return true
+               if d_bottom then
+                       local same = n.name == n_bottom.name
+                       -- Let leveled nodes fall if it can merge with the bottom node
+                       if same and d_bottom.paramtype2 == "leveled" and
+                                       core.get_node_level(p_bottom) <
+                                       core.get_node_max_level(p_bottom) then
+                               convert_to_falling_node(p, n)
+                               return true
+                       end
+                       -- Otherwise only if the bottom node is considered "fall through"
+                       if not same and
+                                       (not d_bottom.walkable or d_bottom.buildable_to) and
+                                       (core.get_item_group(n.name, "float") == 0 or
+                                       d_bottom.liquidtype == "none") then
+                               convert_to_falling_node(p, n)
+                               return true
+                       end
                end
        end
 
index 9c7c42436eb573a6e83247ec1d22415a4dc3a671..8b7c412ab66a49dff2d8345bd097a05bcf712c2b 100644 (file)
@@ -4882,7 +4882,7 @@ Environment access
 * `minetest.add_node_level(pos, level)`
     * increase level of leveled node by level, default `level` equals `1`
     * if `totallevel > maxlevel`, returns rest (`total-max`)
-    * can be negative for decreasing
+    * `level` must be between -127 and 127
 * `minetest.fix_light(pos1, pos2)`: returns `true`/`false`
     * resets the light in a cuboid-shaped part of
       the map and removes lighting bugs.
@@ -7012,11 +7012,15 @@ Used by `minetest.register_node`.
         -- If true, a new liquid source can be created by placing two or more
         -- sources nearby
 
-        leveled = 16,
+        leveled = 0,
         -- Only valid for "nodebox" drawtype with 'type = "leveled"'.
         -- Allows defining the nodebox height without using param2.
         -- The nodebox height is 'leveled' / 64 nodes.
-        -- The maximum value of 'leveled' is 127.
+        -- The maximum value of 'leveled' is `leveled_max`.
+
+        leveled_max = 127,
+        -- Maximum value for `leveled` (0-127), enforced in
+        -- `minetest.set_node_level` and `minetest.add_node_level`.
 
         liquid_range = 8,  -- Number of flowing nodes around source (max. 8)
 
index bf7e79a71894eed0e6ca67f14c56d3cd45f322fb..24d62b5045ccfbf8aeb370370245b8aa965da1f0 100644 (file)
@@ -584,7 +584,7 @@ u8 MapNode::getMaxLevel(const NodeDefManager *nodemgr) const
        if( f.liquid_type == LIQUID_FLOWING || f.param_type_2 == CPT2_FLOWINGLIQUID)
                return LIQUID_LEVEL_MAX;
        if(f.leveled || f.param_type_2 == CPT2_LEVELED)
-               return LEVELED_MAX;
+               return f.leveled_max;
        return 0;
 }
 
@@ -603,14 +603,15 @@ u8 MapNode::getLevel(const NodeDefManager *nodemgr) const
                if (level)
                        return level;
        }
-       if (f.leveled > LEVELED_MAX)
-               return LEVELED_MAX;
+       // Return static value from nodedef if param2 isn't used for level
+       if (f.leveled > f.leveled_max)
+               return f.leveled_max;
        return f.leveled;
 }
 
-u8 MapNode::setLevel(const NodeDefManager *nodemgr, s8 level)
+s8 MapNode::setLevel(const NodeDefManager *nodemgr, s16 level)
 {
-       u8 rest = 0;
+       s8 rest = 0;
        const ContentFeatures &f = nodemgr->get(*this);
        if (f.param_type_2 == CPT2_FLOWINGLIQUID
                        || f.liquid_type == LIQUID_FLOWING
@@ -631,18 +632,18 @@ u8 MapNode::setLevel(const NodeDefManager *nodemgr, s8 level)
                if (level < 0) { // zero means default for a leveled nodebox
                        rest = level;
                        level = 0;
-               } else if (level > LEVELED_MAX) {
-                       rest = level - LEVELED_MAX;
-                       level = LEVELED_MAX;
+               } else if (level > f.leveled_max) {
+                       rest = level - f.leveled_max;
+                       level = f.leveled_max;
                }
                setParam2((level & LEVELED_MASK) | (getParam2() & ~LEVELED_MASK));
        }
        return rest;
 }
 
-u8 MapNode::addLevel(const NodeDefManager *nodemgr, s8 add)
+s8 MapNode::addLevel(const NodeDefManager *nodemgr, s16 add)
 {
-       s8 level = getLevel(nodemgr);
+       s16 level = getLevel(nodemgr);
        level += add;
        return setLevel(nodemgr, level);
 }
index 7a3d30ddc36d21e7ac0a13df5728f2d79c7fa52f..32ac1b4f69a2a7b977502ebd9926614184342087 100644 (file)
@@ -268,12 +268,12 @@ struct MapNode
                std::vector<aabb3f> *boxes, u8 neighbors = 0) const;
 
        /*
-               Liquid helpers
+               Liquid/leveled helpers
        */
        u8 getMaxLevel(const NodeDefManager *nodemgr) const;
        u8 getLevel(const NodeDefManager *nodemgr) const;
-       u8 setLevel(const NodeDefManager *nodemgr, s8 level = 1);
-       u8 addLevel(const NodeDefManager *nodemgr, s8 add = 1);
+       s8 setLevel(const NodeDefManager *nodemgr, s16 level = 1);
+       s8 addLevel(const NodeDefManager *nodemgr, s16 add = 1);
 
        /*
                Serialization functions
index 65199830fe009d34242082c14798f6b521908247..b8211fceb869a38d19ed348e5081bbc95ab26ef3 100644 (file)
@@ -368,6 +368,7 @@ void ContentFeatures::reset()
        floodable = false;
        rightclickable = true;
        leveled = 0;
+       leveled_max = LEVELED_MAX;
        liquid_type = LIQUID_NONE;
        liquid_alternative_flowing = "";
        liquid_alternative_source = "";
@@ -478,6 +479,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
        writeU8(os, legacy_wallmounted);
 
        os << serializeString(node_dig_prediction);
+       writeU8(os, leveled_max);
 }
 
 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
@@ -586,6 +588,10 @@ void ContentFeatures::deSerialize(std::istream &is)
 
        try {
                node_dig_prediction = deSerializeString(is);
+               u8 tmp_leveled_max = readU8(is);
+               if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
+                       throw SerializationError("");
+               leveled_max = tmp_leveled_max;
        } catch(SerializationError &e) {};
 }
 
index 0fce6eab14a3f59ed9acb290cfc3ac5b285c08f1..497e7ee0e01956918795748f6d5f607f5a013036 100644 (file)
@@ -326,8 +326,10 @@ struct ContentFeatures
        std::vector<content_t> connects_to_ids;
        // Post effect color, drawn when the camera is inside the node.
        video::SColor post_effect_color;
-       // Flowing liquid or snow, value = default level
+       // Flowing liquid or leveled nodebox, value = default level
        u8 leveled;
+       // Maximum value for leveled nodes
+       u8 leveled_max;
 
        // --- LIGHTING-RELATED ---
 
index de9634c4235fc1cb9fd8559220f30d5dc4b550dc..116a59c0945a80c1c01af5f60aa889c4b9c6eeb9 100644 (file)
@@ -694,6 +694,8 @@ ContentFeatures read_content_features(lua_State *L, int index)
        f.liquid_range = getintfield_default(L, index,
                        "liquid_range", f.liquid_range);
        f.leveled = getintfield_default(L, index, "leveled", f.leveled);
+       f.leveled_max = getintfield_default(L, index,
+                       "leveled_max", f.leveled_max);
 
        getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
        f.drowning = getintfield_default(L, index,
@@ -860,6 +862,8 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
        lua_setfield(L, -2, "post_effect_color");
        lua_pushnumber(L, c.leveled);
        lua_setfield(L, -2, "leveled");
+       lua_pushnumber(L, c.leveled_max);
+       lua_setfield(L, -2, "leveled_max");
        lua_pushboolean(L, c.sunlight_propagates);
        lua_setfield(L, -2, "sunlight_propagates");
        lua_pushnumber(L, c.light_source);
index b8a8a5ce1a390b29f442128cfc7750ca045bd779..89ec9dc7e4bb989a5a7ca1cde84ea55e1ba53d9a 100644 (file)
@@ -529,13 +529,13 @@ int ModApiEnvMod::l_set_node_level(lua_State *L)
 
 // add_node_level(pos, level)
 // pos = {x=num, y=num, z=num}
-// level: 0..63
+// level: -127..127
 int ModApiEnvMod::l_add_node_level(lua_State *L)
 {
        GET_ENV_PTR;
 
        v3s16 pos = read_v3s16(L, 1);
-       u8 level = 1;
+       s16 level = 1;
        if(lua_isnumber(L, 2))
                level = lua_tonumber(L, 2);
        MapNode n = env->getMap().getNode(pos);