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
-------------------
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.set_armor_groups({level=2, fleshy=2, cracky=2})
object.get_armor_groups() -> a group-rating table (eg. {fleshy=100})
object.set_armor_groups({fleshy=30, cracky=80})
Groups of tools
----------------
@ -522,7 +524,6 @@ 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
@ -575,6 +576,7 @@ groups to enable interaction with tools.
* Uses (until the tool breaks)
* Maximum level (usually 0, 1, 2 or 3)
* Digging times
* Damage groups
**Full punch interval**:
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
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''.
**Damage groups**
List of damage for groups of entities. See "Entity damage mechanism".
Example definition of the capabilities of a tool
-------------------------------------------------
@ -617,6 +620,7 @@ tool_capabilities = {
groupcaps={
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:
@ -647,10 +651,12 @@ Notes:
Entity damage mechanism
------------------------
Damage calculation:
- Take the time spent after the last hit
- Limit time to full_punch_interval
- Take the damage groups and imagine a bunch of nodes that have them
- Damage in HP is the amount of nodes destroyed in this time.
damage = 0
foreach group in cap.damage_groups:
damage += cap.damage_groups[group] * limit(actual_interval / cap.full_punch_interval, 0.0, 1.0)
* (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
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,
groupcaps={
-- 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},
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
}
},
damage_groups = {groupname=damage},
}
node_placement_prediction = nil,
^ 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_ADD_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
#define SERVER_PROTOCOL_VERSION_MIN 13

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

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

@ -80,7 +80,7 @@ struct ItemDefinition
ItemDefinition& operator=(const ItemDefinition &def);
~ItemDefinition();
void reset();
void serialize(std::ostream &os) const;
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
private:
void resetInitial();
@ -109,7 +109,7 @@ public:
IGameDef *gamedef) const=0;
#endif
virtual void serialize(std::ostream &os)=0;
virtual void serialize(std::ostream &os, u16 protocol_version)=0;
};
class IWritableItemDefManager : public IItemDefManager
@ -146,7 +146,7 @@ public:
virtual void registerAlias(const std::string &name,
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;
// 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_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;
}
@ -154,6 +168,16 @@ void set_tool_capabilities(lua_State *L, int table,
}
// Set groupcaps table
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,

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

@ -556,7 +556,7 @@ private:
static void SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target);
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,
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/numeric.h"
void ToolCapabilities::serialize(std::ostream &os) const
void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
{
if(protocol_version <= 17)
writeU8(os, 1); // version
else
writeU8(os, 2); // version
writeF1000(os, full_punch_interval);
writeS16(os, max_drop_level);
writeU32(os, groupcaps.size());
@ -44,12 +47,20 @@ void ToolCapabilities::serialize(std::ostream &os) const
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)
{
int version = readU8(is);
if(version != 1) throw SerializationError(
if(version != 1 && version != 2) throw SerializationError(
"unsupported ToolCapabilities version");
full_punch_interval = readF1000(is);
max_drop_level = readS16(is);
@ -68,6 +79,15 @@ void ToolCapabilities::deSerialize(std::istream &is)
}
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,
@ -136,28 +156,26 @@ DigParams getDigParams(const ItemGroupList &groups,
return getDigParams(groups, tp, 1000000);
}
HitParams getHitParams(const ItemGroupList &groups,
HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp, float time_from_last_punch)
{
DigParams digprop = getDigParams(groups, tp,
time_from_last_punch);
s16 damage = 0;
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;
}
HitParams getHitParams(const ItemGroupList &groups,
return HitParams(damage, 0);
}
HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp)
{
return getHitParams(groups, tp, 1000000);
return getHitParams(armor_groups, tp, 1000000);
}
PunchDamageResult getPunchDamage(
@ -187,7 +205,6 @@ PunchDamageResult getPunchDamage(
result.did_punch = true;
result.wear = hitparams.wear;
result.damage = hitparams.hp;
result.main_group = hitparams.main_group;
}
return result;

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