Rework tool_capabilities a bit (maxwear->uses, scale dig time according to leveldiff)

This commit is contained in:
Perttu Ahola 2012-03-29 13:35:20 +03:00
parent ace005bf7c
commit 440e9cdbef
5 changed files with 137 additions and 75 deletions

@ -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
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
**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.
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 - -
**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).
level diff: 2 1 0 -1 -2
**Maximum level** tells what is the maximum level of a node of this group
that the tool will be able to dig.
Table of resulting tool uses:
-> 0 - - - - -
1 180 60 20 - -
2 180 60 20 - -
3 180 60 20 - -
**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''.
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.

@ -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(),
})

@ -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());
}

@ -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;
}
}

@ -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