Rework tool_capabilities a bit (maxwear->uses, scale dig time according to leveldiff)
authorPerttu Ahola <celeron55@gmail.com>
Thu, 29 Mar 2012 10:35:20 +0000 (13:35 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Thu, 29 Mar 2012 10:35:20 +0000 (13:35 +0300)
doc/lua_api.txt
games/minimal/mods/default/init.lua
src/scriptapi.cpp
src/tool.cpp
src/tool.h

index a8386406e7e3bd8a126e65cd1b7f9f0e44ca6e9b..c09a5815faa5e0bf16ca5b75442f2f23aeca4302 100644 (file)
@@ -255,8 +255,8 @@ Groups of items can define what kind of an item it is (eg. wool).
 
 Groups of nodes
 ----------------
-In addition to the general item things, whether a node is diggable and how
-long it takes is defined by using groups.
+In addition to the general item things, groups are used to define whether
+a node is destroyable and how long it takes to destroy by a tool.
 
 Groups of entities
 -------------------
@@ -292,6 +292,7 @@ Special groups
     damage, and get weared out much faster, or not be able to get drops
        from destroyed nodes.
   - 0 is something that is directly accessible at the start of gameplay
+  - There is no upper limit
 - dig_immediate: (player can always pick up node without tool wear)
   - 2: node is removed without tool wear after 0.5 seconds or so
        (rail, sign)
@@ -299,6 +300,7 @@ Special groups
 
 Known damage and digging time defining groups
 ----------------------------------------------
+Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated.
 - crumbly: dirt, sand
 - cracky: tough but crackable stuff like stone.
 - snappy: something that can be cut using fine tools; eg. leaves, small
@@ -334,59 +336,105 @@ Groups such as **crumbly**, **cracky** and **snappy** are used for this
 purpose. Rating is 1, 2 or 3. A higher rating for such a group implies
 faster digging time.
 
-Also, the **level** group is used.
+The **level** group is used to limit the toughness of nodes a tool can dig
+and to scale the digging times / damage to a greater extent.
+
+^ PLEASE DO UNDERSTAND THIS, otherwise you cannot use the system to it's
+  full potential.
 
 Tools define their properties by a list of parameters for groups. They
 cannot dig other groups; thus it is important to use a standard bunch of
 groups to enable interaction with tools.
 
-**Example definition of the digging capabilities of a tool:**
+**Tools define:**
+  * Full punch interval
+  * Maximum drop level
+  * For an arbitrary list of groups:
+    * Uses (until the tool breaks)
+    * Maximum level (usually 0, 1, 2 or 3)
+    * Digging times
+
+**Full punch interval**:
+When used as a weapon, the tool will do full damage if this time is spent
+between punches. If eg. half the time is spent, the tool will do half
+damage.
+
+**Maximum drop level**
+Suggests the maximum level of node, when dug with the tool, that will drop
+it's useful item. (eg. iron ore to drop a lump of iron).
+- This is not automated; it is the responsibility of the node definition
+  to implement this
+
+**Uses**
+Determines how many uses the tool has when it is used for digging a node,
+of this group, of the maximum level. For lower leveled nodes, the use count
+is multiplied by 3^leveldiff.
+- uses=10, leveldiff=0 -> actual_uses=10
+- uses=10, leveldiff=1 -> actual_uses=30
+- uses=10, leveldiff=2 -> actual_uses=90
+
+**Maximum level**
+Tells what is the maximum level of a node of this group that the tool will
+be able to dig.
+
+**Digging times**
+List of digging times for different ratings of the group, for nodes of the
+maximum level.
+  * For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would
+    result the tool to be able to dig nodes that have a rating of 2 or 3
+    for this group, and unable to dig the rating 1, which is the toughest.
+    Unless there is a matching group that enables digging otherwise.
+  * For entities, damage equals the amount of nodes dug in the time spent
+    between hits, with a maximum time of ''full_punch_interval''.
+
+Example definition of the capabilities of a tool
+-------------------------------------------------
 tool_capabilities = {
        full_punch_interval=1.5,
        max_drop_level=1,
        groupcaps={
-               crumbly={maxwear=0.01, maxlevel=2, times={[1]=0.80, [2]=0.60, [3]=0.40}}
+               crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
        }
 }
 
-**Tools define:**
-  * Full punch interval Maximum drop level For an arbitrary list of groups:
-  * Maximum level (usually 0, 1, 2 or 3) Maximum wear (0...1) Digging times
-
-**Full punch interval**: When used as a weapon, the tool will do full
-damage if this time is spent between punches. If eg. half the time is
-spent, the tool will do half damage.
-
-**Maximum drop level** suggests the maximum level of node, when dug with
-the tool, that will drop it's useful item. (eg. iron ore to drop a lump of
-iron).
-
-**Maximum level** tells what is the maximum level of a node of this group
-that the tool will be able to dig.
-
-**Maximum wear** determines how much the tool wears out when it is used for
-digging a node, of this group, of the maximum level. For lower leveled
-tools, the wear is divided by //4// to the exponent //level difference//.
-This means for a maximum wear of 0.1, a level difference 1 will result in
-wear=0.1/4=0.025, and a level difference of 2 will result in
-wear=0.1/(4*4)=0.00625.
-
-**Digging times** is basically a list of digging times for different
-ratings of the group. It also determines the damage done to entities, based
-on their "armor groups".
-  * For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would
-  * result the tool to be able to dig nodes that have a rating of 2 or 3
-  * for this group, and unable to dig the rating 1, which is the toughest.
-  * Unless there is a matching group that enables digging otherwise.  For
-  * entities, damage equals the amount of nodes dug in the time spent
-  * between hits, with a maximum of ''full_punch_interval''.
+This makes the tool be able to dig nodes that fullfill both of these:
+- Have the **crumbly** group
+- Have a **level** group less or equal to 2
+
+Table of resulting digging times:
+crumbly        0     1     2     3     4  <- level
+     ->  0     -     -     -     -     -
+         1  0.80  1.60  1.60     -     -
+         2  0.60  1.20  1.20     -     -
+         3  0.40  0.80  0.80     -     -
+
+level diff:    2     1     0    -1    -2
+
+Table of resulting tool uses:
+     ->  0     -     -     -     -     -
+         1   180    60    20     -     -
+         2   180    60    20     -     -
+         3   180    60    20     -     -
+
+Notes:
+- At crumbly=0, the node is not diggable.
+- At crumbly=3, the level difference digging time divider kicks in and makes
+  easy nodes to be quickly breakable.
+- At level > 2, the node is not diggable, because it's level > maxlevel
 
 Entity damage mechanism
 ------------------------
+Damage calculation:
+- Take the time spent after the last hit
+- Limit time to full_punch_interval
+- Take the damage groups, assume a node has them
+- Damage in HP is the amount of nodes destroyed in this time.
+
 Client predicts damage based on damage groups. Because of this, it is able to
 give an immediate response when an entity is damaged or dies; the response is
 pre-defined somehow (eg. by defining a sprite animation) (not implemented;
 TODO).
+- Currently a smoke puff will appear when an entity dies.
 
 The group **immortal** will completely disable normal damage.
 
index 932e90deead8fa32358dd3d6e7d9a5f9d54f7cd3..b408e8cce76bc334d8606c875049089246dcde2e 100644 (file)
@@ -24,10 +24,10 @@ minetest.register_item(":", {
                full_punch_interval = 1.0,
                max_drop_level = 0,
                groupcaps = {
-                       fleshy = {times={[2]=2.00, [3]=1.00}, maxwear=0, maxlevel=1},
-                       crumbly = {times={[3]=0.70}, maxwear=0, maxlevel=1},
-                       snappy = {times={[3]=0.40}, maxwear=0, maxlevel=1},
-                       oddly_breakable_by_hand = {times={[1]=3.50,[2]=2.00,[3]=0.70}, maxwear=0, maxlevel=3},
+                       fleshy = {times={[2]=2.00, [3]=1.00}, uses=0, maxlevel=1},
+                       crumbly = {times={[2]=3.00, [3]=0.70}, uses=0, maxlevel=1},
+                       snappy = {times={[3]=0.40}, uses=0, maxlevel=1},
+                       oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=1.40}, uses=0, maxlevel=3},
                }
        }
 })
@@ -38,7 +38,7 @@ minetest.register_tool("default:pick_wood", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       cracky={times={[2]=1.50, [3]=0.80}, maxwear=0.1, maxlevel=1}
+                       cracky={times={[2]=2.00, [3]=1.20}, uses=10, maxlevel=1}
                }
        },
 })
@@ -48,7 +48,7 @@ minetest.register_tool("default:pick_stone", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       cracky={times={[1]=1.50, [2]=0.80, [3]=0.60}, maxwear=0.05, maxlevel=1}
+                       cracky={times={[1]=2.00, [2]=1.20, [3]=0.80}, uses=20, maxlevel=1}
                }
        },
 })
@@ -58,7 +58,7 @@ minetest.register_tool("default:pick_steel", {
        tool_capabilities = {
                max_drop_level=1,
                groupcaps={
-                       cracky={times={[1]=1.00, [2]=0.60, [3]=0.40}, maxwear=0.1, maxlevel=2}
+                       cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2}
                }
        },
 })
@@ -69,9 +69,9 @@ minetest.register_tool("default:pick_mese", {
                full_punch_interval = 1.0,
                max_drop_level=3,
                groupcaps={
-                       cracky={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3},
-                       crumbly={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3},
-                       snappy={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3}
+                       cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
+                       crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
+                       snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}
                }
        },
 })
@@ -81,7 +81,7 @@ minetest.register_tool("default:shovel_wood", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       crumbly={times={[1]=1.50, [2]=0.80, [3]=0.50}, maxwear=0.1, maxlevel=1}
+                       crumbly={times={[1]=2.00, [2]=0.80, [3]=0.50}, uses=10, maxlevel=1}
                }
        },
 })
@@ -91,7 +91,7 @@ minetest.register_tool("default:shovel_stone", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       crumbly={times={[1]=0.80, [2]=0.50, [3]=0.30}, maxwear=0.05, maxlevel=1}
+                       crumbly={times={[1]=1.20, [2]=0.50, [3]=0.30}, uses=20, maxlevel=1}
                }
        },
 })
@@ -101,7 +101,7 @@ minetest.register_tool("default:shovel_steel", {
        tool_capabilities = {
                max_drop_level=1,
                groupcaps={
-                       crumbly={times={[1]=0.50, [2]=0.35, [3]=0.30}, maxwear=0.1, maxlevel=2}
+                       crumbly={times={[1]=1.00, [2]=0.70, [3]=0.60}, uses=10, maxlevel=2}
                }
        },
 })
@@ -111,8 +111,8 @@ minetest.register_tool("default:axe_wood", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       choppy={times={[2]=1.40, [3]=0.80}, maxwear=0.1, maxlevel=1},
-                       fleshy={times={[2]=1.50, [3]=0.80}, maxwear=0.1, maxlevel=1}
+                       choppy={times={[2]=1.40, [3]=0.80}, uses=10, maxlevel=1},
+                       fleshy={times={[2]=1.50, [3]=0.80}, uses=10, maxlevel=1}
                }
        },
 })
@@ -122,8 +122,8 @@ minetest.register_tool("default:axe_stone", {
        tool_capabilities = {
                max_drop_level=0,
                groupcaps={
-                       choppy={times={[1]=1.50, [2]=1.00, [3]=0.60}, maxwear=0.05, maxlevel=1},
-                       fleshy={times={[2]=1.30, [3]=0.70}, maxwear=0.05, maxlevel=1}
+                       choppy={times={[1]=1.50, [2]=1.00, [3]=0.60}, uses=20, maxlevel=1},
+                       fleshy={times={[2]=1.30, [3]=0.70}, uses=20, maxlevel=1}
                }
        },
 })
@@ -133,8 +133,8 @@ minetest.register_tool("default:axe_steel", {
        tool_capabilities = {
                max_drop_level=1,
                groupcaps={
-                       choppy={times={[1]=1.00, [2]=0.80, [3]=0.50}, maxwear=0.1, maxlevel=2},
-                       fleshy={times={[2]=1.10, [3]=0.60}, maxwear=0.03, maxlevel=1}
+                       choppy={times={[1]=2.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2},
+                       fleshy={times={[2]=1.10, [3]=0.60}, uses=40, maxlevel=1}
                }
        },
 })
@@ -145,9 +145,9 @@ minetest.register_tool("default:sword_wood", {
                full_punch_interval = 1.0,
                max_drop_level=0,
                groupcaps={
-                       fleshy={times={[2]=1.10, [3]=0.60}, maxwear=0.1, maxlevel=1},
-                       snappy={times={[2]=1.00, [3]=0.50}, maxwear=0.1, maxlevel=1},
-                       choppy={times={[3]=1.00}, maxwear=0.05, maxlevel=0}
+                       fleshy={times={[2]=1.10, [3]=0.60}, uses=10, maxlevel=1},
+                       snappy={times={[2]=1.00, [3]=0.50}, uses=10, maxlevel=1},
+                       choppy={times={[3]=1.00}, uses=20, maxlevel=0}
                }
        }
 })
@@ -158,9 +158,9 @@ minetest.register_tool("default:sword_stone", {
                full_punch_interval = 1.0,
                max_drop_level=0,
                groupcaps={
-                       fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
-                       snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
-                       choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
+                       fleshy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1},
+                       snappy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1},
+                       choppy={times={[3]=0.90}, uses=20, maxlevel=0}
                }
        }
 })
@@ -171,9 +171,9 @@ minetest.register_tool("default:sword_steel", {
                full_punch_interval = 1.0,
                max_drop_level=1,
                groupcaps={
-                       fleshy={times={[1]=1.00, [2]=0.40, [3]=0.20}, maxwear=0.1, maxlevel=2},
-                       snappy={times={[2]=0.70, [3]=0.30}, maxwear=0.03, maxlevel=1},
-                       choppy={times={[3]=0.70}, maxwear=0.03, maxlevel=0}
+                       fleshy={times={[1]=2.00, [2]=0.80, [3]=0.40}, uses=10, maxlevel=2},
+                       snappy={times={[2]=0.70, [3]=0.30}, uses=40, maxlevel=1},
+                       choppy={times={[3]=0.70}, uses=40, maxlevel=0}
                }
        }
 })
@@ -962,7 +962,7 @@ minetest.register_node("default:mese", {
        description = "Mese",
        tile_images = {"default_mese.png"},
        is_ground_content = true,
-       groups = {cracky=1},
+       groups = {cracky=1,level=2},
        sounds = default.node_sound_defaults(),
 })
 
index 38d681e9b8032545d24490eee29ec591a2053b16..863e2ba288af2d1ced20f600f61cc657303dc0bf 100644 (file)
@@ -670,8 +670,19 @@ static ToolCapabilities read_tool_capabilities(
                                // This will be created
                                ToolGroupCap groupcap;
                                // Read simple parameters
-                               getfloatfield(L, table_groupcap, "maxwear", groupcap.maxwear);
                                getintfield(L, table_groupcap, "maxlevel", groupcap.maxlevel);
+                               getintfield(L, table_groupcap, "uses", groupcap.uses);
+                               // DEPRECATED: maxwear
+                               float maxwear = 0;
+                               if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){
+                                       if(maxwear != 0)
+                                               groupcap.uses = 1.0/maxwear;
+                                       else
+                                               groupcap.uses = 0;
+                                       infostream<<script_get_backtrace(L)<<std::endl;
+                                       infostream<<"WARNING: field \"maxwear\" is deprecated; "
+                                                       <<"should replace with uses=1/maxwear"<<std::endl;
+                               }
                                // Read "times" table
                                lua_getfield(L, table_groupcap, "times");
                                if(lua_istable(L, -1)){
@@ -725,8 +736,8 @@ static void set_tool_capabilities(lua_State *L, int table,
                // Set subtable "times"
                lua_setfield(L, -2, "times");
                // Set simple parameters
-               setfloatfield(L, -1, "maxwear", groupcap.maxwear);
                setintfield(L, -1, "maxlevel", groupcap.maxlevel);
+               setintfield(L, -1, "uses", groupcap.uses);
                // Insert groupcap table into groupcaps table
                lua_setfield(L, -2, name.c_str());
        }
index dcd4fbdf8b9567dafa2cdf6b6e34c1e613801a5e..2488e0ba2e3015238b6c32193e22b74a69bc3b75 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 void ToolCapabilities::serialize(std::ostream &os) const
 {
-       writeU8(os, 0); // version
+       writeU8(os, 1); // version
        writeF1000(os, full_punch_interval);
        writeS16(os, max_drop_level);
        writeU32(os, groupcaps.size());
@@ -34,8 +34,8 @@ void ToolCapabilities::serialize(std::ostream &os) const
                const std::string *name = &i->first;
                const ToolGroupCap *cap = &i->second;
                os<<serializeString(*name);
-               writeF1000(os, cap->maxwear);
-               writeF1000(os, cap->maxlevel);
+               writeS16(os, cap->uses);
+               writeS16(os, cap->maxlevel);
                writeU32(os, cap->times.size());
                for(std::map<int, float>::const_iterator
                                i = cap->times.begin(); i != cap->times.end(); i++){
@@ -48,7 +48,7 @@ void ToolCapabilities::serialize(std::ostream &os) const
 void ToolCapabilities::deSerialize(std::istream &is)
 {
        int version = readU8(is);
-       if(version != 0) throw SerializationError(
+       if(version != 1) throw SerializationError(
                        "unsupported ToolCapabilities version");
        full_punch_interval = readF1000(is);
        max_drop_level = readS16(is);
@@ -57,8 +57,8 @@ void ToolCapabilities::deSerialize(std::istream &is)
        for(u32 i=0; i<groupcaps_size; i++){
                std::string name = deSerializeString(is);
                ToolGroupCap cap;
-               cap.maxwear = readF1000(is);
-               cap.maxlevel = readF1000(is);
+               cap.uses = readS16(is);
+               cap.maxlevel = readS16(is);
                u32 times_size = readU32(is);
                for(u32 i=0; i<times_size; i++){
                        int level = readS16(is);
@@ -102,11 +102,14 @@ DigParams getDigParams(const ItemGroupList &groups,
                float time = 0;
                bool time_exists = cap.getTime(rating, &time);
                if(!result_diggable || time < result_time){
-                       if(cap.maxlevel > level && time_exists){
+                       if(cap.maxlevel >= level && time_exists){
                                result_diggable = true;
-                               result_time = time;
                                int leveldiff = cap.maxlevel - level;
-                               result_wear = cap.maxwear / pow(4.0, (double)leveldiff);
+                               result_time = time / MYMAX(1, leveldiff);
+                               if(cap.uses != 0)
+                                       result_wear = 1.0 / cap.uses / pow(3.0, (double)leveldiff);
+                               else
+                                       result_wear = 0;
                                result_main_group = name;
                        }
                }
index a92a77294fb731e368441a77be5eab1c143f744c..32f1962d04d1208d6d05f6bfac99989ea694785d 100644 (file)
@@ -29,12 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 struct ToolGroupCap
 {
        std::map<int, float> times;
-       float maxwear;
        int maxlevel;
+       int uses;
 
        ToolGroupCap():
-               maxwear(0.05),
-               maxlevel(1)
+               maxlevel(1),
+               uses(20)
        {}
 
        bool getTime(int rating, float *time) const