New damage system, add damageGroups to ToolCapabilities, bump protocol version

This commit is contained in:
PilzAdam 2013-03-28 21:40:44 +01:00
parent 3640c8c051
commit 7d9329ecfe
10 changed files with 104 additions and 56 deletions

@ -471,9 +471,11 @@ a node is destroyable and how long it takes to destroy by a tool.
Groups of entities Groups of entities
------------------- -------------------
For entities, groups are, as of now, used only for calculating damage. For entities, groups are, as of now, used only for calculating damage.
The rating is the percentage of damage caused by tools with this damage group.
See "Entity damage mechanism".
object.get_armor_groups() -> a group-rating table (eg. {fleshy=3}) object.get_armor_groups() -> a group-rating table (eg. {fleshy=100})
object.set_armor_groups({level=2, fleshy=2, cracky=2}) object.set_armor_groups({fleshy=30, cracky=80})
Groups of tools Groups of tools
---------------- ----------------
@ -522,7 +524,6 @@ Special groups
Known damage and digging time defining groups Known damage and digging time defining groups
---------------------------------------------- ----------------------------------------------
Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated.
- crumbly: dirt, sand - crumbly: dirt, sand
- cracky: tough but crackable stuff like stone. - cracky: tough but crackable stuff like stone.
- snappy: something that can be cut using fine tools; eg. leaves, small - snappy: something that can be cut using fine tools; eg. leaves, small
@ -575,6 +576,7 @@ groups to enable interaction with tools.
* Uses (until the tool breaks) * Uses (until the tool breaks)
* Maximum level (usually 0, 1, 2 or 3) * Maximum level (usually 0, 1, 2 or 3)
* Digging times * Digging times
* Damage groups
**Full punch interval**: **Full punch interval**:
When used as a weapon, the tool will do full damage if this time is spent When used as a weapon, the tool will do full damage if this time is spent
@ -606,8 +608,9 @@ maximum level.
result in the tool to be able to dig nodes that have a rating of 2 or 3 result in 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. for this group, and unable to dig the rating 1, which is the toughest.
Unless there is a matching group that enables digging otherwise. 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''. **Damage groups**
List of damage for groups of entities. See "Entity damage mechanism".
Example definition of the capabilities of a tool Example definition of the capabilities of a tool
------------------------------------------------- -------------------------------------------------
@ -617,6 +620,7 @@ tool_capabilities = {
groupcaps={ groupcaps={
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
} }
damage_groups = {fleshy=2},
} }
This makes the tool be able to dig nodes that fullfill both of these: This makes the tool be able to dig nodes that fullfill both of these:
@ -647,10 +651,12 @@ Notes:
Entity damage mechanism Entity damage mechanism
------------------------ ------------------------
Damage calculation: Damage calculation:
- Take the time spent after the last hit damage = 0
- Limit time to full_punch_interval foreach group in cap.damage_groups:
- Take the damage groups and imagine a bunch of nodes that have them damage += cap.damage_groups[group] * limit(actual_interval / cap.full_punch_interval, 0.0, 1.0)
- Damage in HP is the amount of nodes destroyed in this time. * (object.armor_groups[group] / 100.0)
-- Where object.armor_groups[group] is 0 for inexisting values
return damage
Client predicts damage based on damage groups. Because of this, it is able to 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 give an immediate response when an entity is damaged or dies; the response is
@ -1496,10 +1502,10 @@ Item definition (register_node, register_craftitem, register_tool)
max_drop_level=0, max_drop_level=0,
groupcaps={ groupcaps={
-- For example: -- For example:
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}, snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0} choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
} },
damage_groups = {groupname=damage},
} }
node_placement_prediction = nil, node_placement_prediction = nil,
^ If nil and item is node, prediction is made automatically ^ If nil and item is node, prediction is made automatically

@ -85,9 +85,11 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
TOCLIENT_SPAWN_PARTICLE TOCLIENT_SPAWN_PARTICLE
TOCLIENT_ADD_PARTICLESPAWNER TOCLIENT_ADD_PARTICLESPAWNER
TOCLIENT_DELETE_PARTICLESPAWNER TOCLIENT_DELETE_PARTICLESPAWNER
PROTOCOL_VERSION 18:
damageGroups added to ToolCapabilities
*/ */
#define LATEST_PROTOCOL_VERSION 17 #define LATEST_PROTOCOL_VERSION 18
// Server's supported network protocol range // Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13 #define SERVER_PROTOCOL_VERSION_MIN 13

@ -381,8 +381,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
} }
// Initialize something to armor groups // Initialize something to armor groups
m_armor_groups["fleshy"] = 3; m_armor_groups["fleshy"] = 100;
m_armor_groups["snappy"] = 2;
} }
LuaEntitySAO::~LuaEntitySAO() LuaEntitySAO::~LuaEntitySAO()
@ -942,8 +941,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
assert(m_peer_id != 0); assert(m_peer_id != 0);
setBasePosition(m_player->getPosition()); setBasePosition(m_player->getPosition());
m_inventory = &m_player->inventory; m_inventory = &m_player->inventory;
m_armor_groups["choppy"] = 2; m_armor_groups["fleshy"] = 100;
m_armor_groups["fleshy"] = 3;
m_prop.hp_max = PLAYER_MAX_HP; m_prop.hp_max = PLAYER_MAX_HP;
m_prop.physical = false; m_prop.physical = false;

@ -111,7 +111,7 @@ void ItemDefinition::reset()
node_placement_prediction = ""; node_placement_prediction = "";
} }
void ItemDefinition::serialize(std::ostream &os) const void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
{ {
writeU8(os, 1); // version writeU8(os, 1); // version
writeU8(os, type); writeU8(os, type);
@ -126,7 +126,7 @@ void ItemDefinition::serialize(std::ostream &os) const
std::string tool_capabilities_s = ""; std::string tool_capabilities_s = "";
if(tool_capabilities){ if(tool_capabilities){
std::ostringstream tmp_os(std::ios::binary); std::ostringstream tmp_os(std::ios::binary);
tool_capabilities->serialize(tmp_os); tool_capabilities->serialize(tmp_os, protocol_version);
tool_capabilities_s = tmp_os.str(); tool_capabilities_s = tmp_os.str();
} }
os<<serializeString(tool_capabilities_s); os<<serializeString(tool_capabilities_s);
@ -547,7 +547,7 @@ public:
m_aliases[name] = convert_to; m_aliases[name] = convert_to;
} }
} }
void serialize(std::ostream &os) void serialize(std::ostream &os, u16 protocol_version)
{ {
writeU8(os, 0); // version writeU8(os, 0); // version
u16 count = m_item_definitions.size(); u16 count = m_item_definitions.size();
@ -559,7 +559,7 @@ public:
ItemDefinition *def = i->second; ItemDefinition *def = i->second;
// Serialize ItemDefinition and write wrapped in a string // Serialize ItemDefinition and write wrapped in a string
std::ostringstream tmp_os(std::ios::binary); std::ostringstream tmp_os(std::ios::binary);
def->serialize(tmp_os); def->serialize(tmp_os, protocol_version);
os<<serializeString(tmp_os.str()); os<<serializeString(tmp_os.str());
} }
writeU16(os, m_aliases.size()); writeU16(os, m_aliases.size());

@ -80,7 +80,7 @@ struct ItemDefinition
ItemDefinition& operator=(const ItemDefinition &def); ItemDefinition& operator=(const ItemDefinition &def);
~ItemDefinition(); ~ItemDefinition();
void reset(); void reset();
void serialize(std::ostream &os) const; void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
private: private:
void resetInitial(); void resetInitial();
@ -109,7 +109,7 @@ public:
IGameDef *gamedef) const=0; IGameDef *gamedef) const=0;
#endif #endif
virtual void serialize(std::ostream &os)=0; virtual void serialize(std::ostream &os, u16 protocol_version)=0;
}; };
class IWritableItemDefManager : public IItemDefManager class IWritableItemDefManager : public IItemDefManager
@ -146,7 +146,7 @@ public:
virtual void registerAlias(const std::string &name, virtual void registerAlias(const std::string &name,
const std::string &convert_to)=0; const std::string &convert_to)=0;
virtual void serialize(std::ostream &os)=0; virtual void serialize(std::ostream &os, u16 protocol_version)=0;
virtual void deSerialize(std::istream &is)=0; virtual void deSerialize(std::istream &is)=0;
// Do stuff asked by threads that can only be done in the main thread // Do stuff asked by threads that can only be done in the main thread

@ -117,6 +117,20 @@ ToolCapabilities read_tool_capabilities(
} }
} }
lua_pop(L, 1); lua_pop(L, 1);
lua_getfield(L, table, "damage_groups");
if(lua_istable(L, -1)){
int table_damage_groups = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table_damage_groups) != 0){
// key at index -2 and value at index -1
std::string groupname = luaL_checkstring(L, -2);
u16 value = luaL_checkinteger(L, -1);
toolcap.damageGroups[groupname] = value;
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
lua_pop(L, 1);
return toolcap; return toolcap;
} }
@ -154,6 +168,16 @@ void set_tool_capabilities(lua_State *L, int table,
} }
// Set groupcaps table // Set groupcaps table
lua_setfield(L, -2, "groupcaps"); lua_setfield(L, -2, "groupcaps");
//Create damage_groups table
lua_newtable(L);
// For each damage group
for(std::map<std::string, s16>::const_iterator
i = toolcap.damageGroups.begin(); i != toolcap.damageGroups.end(); i++){
// Create damage group table
lua_pushinteger(L, i->second);
lua_setfield(L, -2, i->first.c_str());
}
lua_setfield(L, -2, "damage_groups");
} }
void push_tool_capabilities(lua_State *L, void push_tool_capabilities(lua_State *L,

@ -2080,7 +2080,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
SendMovement(m_con, peer_id); SendMovement(m_con, peer_id);
// Send item definitions // Send item definitions
SendItemDef(m_con, peer_id, m_itemdef); SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
// Send node definitions // Send node definitions
SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
@ -3342,7 +3342,7 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
} }
void Server::SendItemDef(con::Connection &con, u16 peer_id, void Server::SendItemDef(con::Connection &con, u16 peer_id,
IItemDefManager *itemdef) IItemDefManager *itemdef, u16 protocol_version)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
@ -3354,7 +3354,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id,
*/ */
writeU16(os, TOCLIENT_ITEMDEF); writeU16(os, TOCLIENT_ITEMDEF);
std::ostringstream tmp_os(std::ios::binary); std::ostringstream tmp_os(std::ios::binary);
itemdef->serialize(tmp_os); itemdef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary); std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2); compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str()); os<<serializeLongString(tmp_os2.str());

@ -556,7 +556,7 @@ private:
static void SendDeathscreen(con::Connection &con, u16 peer_id, static void SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target); bool set_camera_point_target, v3f camera_point_target);
static void SendItemDef(con::Connection &con, u16 peer_id, static void SendItemDef(con::Connection &con, u16 peer_id,
IItemDefManager *itemdef); IItemDefManager *itemdef, u16 protocol_version);
static void SendNodeDef(con::Connection &con, u16 peer_id, static void SendNodeDef(con::Connection &con, u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version); INodeDefManager *nodedef, u16 protocol_version);

@ -24,9 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h" #include "util/serialize.h"
#include "util/numeric.h" #include "util/numeric.h"
void ToolCapabilities::serialize(std::ostream &os) const void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
{ {
writeU8(os, 1); // version if(protocol_version <= 17)
writeU8(os, 1); // version
else
writeU8(os, 2); // version
writeF1000(os, full_punch_interval); writeF1000(os, full_punch_interval);
writeS16(os, max_drop_level); writeS16(os, max_drop_level);
writeU32(os, groupcaps.size()); writeU32(os, groupcaps.size());
@ -44,12 +47,20 @@ void ToolCapabilities::serialize(std::ostream &os) const
writeF1000(os, i->second); writeF1000(os, i->second);
} }
} }
if(protocol_version > 17){
writeU32(os, damageGroups.size());
for(std::map<std::string, s16>::const_iterator
i = damageGroups.begin(); i != damageGroups.end(); i++){
os<<serializeString(i->first);
writeS16(os, i->second);
}
}
} }
void ToolCapabilities::deSerialize(std::istream &is) void ToolCapabilities::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 1) throw SerializationError( if(version != 1 && version != 2) throw SerializationError(
"unsupported ToolCapabilities version"); "unsupported ToolCapabilities version");
full_punch_interval = readF1000(is); full_punch_interval = readF1000(is);
max_drop_level = readS16(is); max_drop_level = readS16(is);
@ -68,6 +79,15 @@ void ToolCapabilities::deSerialize(std::istream &is)
} }
groupcaps[name] = cap; groupcaps[name] = cap;
} }
if(version == 2)
{
u32 damage_groups_size = readU32(is);
for(u32 i=0; i<damage_groups_size; i++){
std::string name = deSerializeString(is);
s16 rating = readS16(is);
damageGroups[name] = rating;
}
}
} }
DigParams getDigParams(const ItemGroupList &groups, DigParams getDigParams(const ItemGroupList &groups,
@ -136,28 +156,26 @@ DigParams getDigParams(const ItemGroupList &groups,
return getDigParams(groups, tp, 1000000); return getDigParams(groups, tp, 1000000);
} }
HitParams getHitParams(const ItemGroupList &groups, HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp, float time_from_last_punch) const ToolCapabilities *tp, float time_from_last_punch)
{ {
DigParams digprop = getDigParams(groups, tp, s16 damage = 0;
time_from_last_punch); float full_punch_interval = tp->full_punch_interval;
if(time_from_last_punch > tp->full_punch_interval)
time_from_last_punch = tp->full_punch_interval;
// Damage in hp is equivalent to nodes dug in time_from_last_punch
s16 hp = 0;
if(digprop.diggable)
hp = time_from_last_punch / digprop.time;
// Wear is the same as for digging a single node
s16 wear = (float)digprop.wear;
return HitParams(hp, wear, digprop.main_group); for(std::map<std::string, s16>::const_iterator
i = tp->damageGroups.begin(); i != tp->damageGroups.end(); i++){
s16 armor = itemgroup_get(armor_groups, i->first);
damage += i->second * rangelim(time_from_last_punch * full_punch_interval, 0.0, 1.0)
* armor / 100.0;
}
return HitParams(damage, 0);
} }
HitParams getHitParams(const ItemGroupList &groups, HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp) const ToolCapabilities *tp)
{ {
return getHitParams(groups, tp, 1000000); return getHitParams(armor_groups, tp, 1000000);
} }
PunchDamageResult getPunchDamage( PunchDamageResult getPunchDamage(
@ -187,7 +205,6 @@ PunchDamageResult getPunchDamage(
result.did_punch = true; result.did_punch = true;
result.wear = hitparams.wear; result.wear = hitparams.wear;
result.damage = hitparams.hp; result.damage = hitparams.hp;
result.main_group = hitparams.main_group;
} }
return result; return result;

@ -52,6 +52,7 @@ struct ToolGroupCap
// CLANG SUCKS DONKEY BALLS // CLANG SUCKS DONKEY BALLS
typedef std::map<std::string, struct ToolGroupCap> ToolGCMap; typedef std::map<std::string, struct ToolGroupCap> ToolGCMap;
typedef std::map<std::string, s16> DamageGroup;
struct ToolCapabilities struct ToolCapabilities
{ {
@ -59,19 +60,22 @@ struct ToolCapabilities
int max_drop_level; int max_drop_level;
// CLANG SUCKS DONKEY BALLS // CLANG SUCKS DONKEY BALLS
ToolGCMap groupcaps; ToolGCMap groupcaps;
DamageGroup damageGroups;
ToolCapabilities( ToolCapabilities(
float full_punch_interval_=1.4, float full_punch_interval_=1.4,
int max_drop_level_=1, int max_drop_level_=1,
// CLANG SUCKS DONKEY BALLS // CLANG SUCKS DONKEY BALLS
ToolGCMap groupcaps_=ToolGCMap() ToolGCMap groupcaps_=ToolGCMap(),
DamageGroup damageGroups_=DamageGroup()
): ):
full_punch_interval(full_punch_interval_), full_punch_interval(full_punch_interval_),
max_drop_level(max_drop_level_), max_drop_level(max_drop_level_),
groupcaps(groupcaps_) groupcaps(groupcaps_),
damageGroups(damageGroups_)
{} {}
void serialize(std::ostream &os) const; void serialize(std::ostream &os, u16 version) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
}; };
@ -103,19 +107,17 @@ struct HitParams
{ {
s16 hp; s16 hp;
s16 wear; s16 wear;
std::string main_group;
HitParams(s16 hp_=0, s16 wear_=0, std::string main_group_=""): HitParams(s16 hp_=0, s16 wear_=0):
hp(hp_), hp(hp_),
wear(wear_), wear(wear_)
main_group(main_group_)
{} {}
}; };
HitParams getHitParams(const ItemGroupList &groups, HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp, float time_from_last_punch); const ToolCapabilities *tp, float time_from_last_punch);
HitParams getHitParams(const ItemGroupList &groups, HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp); const ToolCapabilities *tp);
struct PunchDamageResult struct PunchDamageResult
@ -123,7 +125,6 @@ struct PunchDamageResult
bool did_punch; bool did_punch;
int damage; int damage;
int wear; int wear;
std::string main_group;
PunchDamageResult(): PunchDamageResult():
did_punch(false), did_punch(false),