3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "itemgroup.h"
24 #include "inventory.h"
25 #include "exceptions.h"
26 #include "util/serialize.h"
27 #include "util/numeric.h"
29 void ToolGroupCap::toJson(Json::Value
&object
) const
31 object
["maxlevel"] = maxlevel
;
32 object
["uses"] = uses
;
34 Json::Value times_object
;
35 for (auto time
: times
)
36 times_object
[time
.first
] = time
.second
;
37 object
["times"] = times_object
;
40 void ToolGroupCap::fromJson(const Json::Value
&json
)
42 if (json
.isObject()) {
43 if (json
["maxlevel"].isInt())
44 maxlevel
= json
["maxlevel"].asInt();
45 if (json
["uses"].isInt())
46 uses
= json
["uses"].asInt();
47 const Json::Value
×_object
= json
["times"];
48 if (times_object
.isArray()) {
49 Json::ArrayIndex size
= times_object
.size();
50 for (Json::ArrayIndex i
= 0; i
< size
; ++i
)
51 if (times_object
[i
].isDouble())
52 times
[i
] = times_object
[i
].asFloat();
57 void ToolCapabilities::serialize(std::ostream
&os
, u16 protocol_version
) const
59 if (protocol_version
>= 38)
62 writeU8(os
, 4); // proto == 37
63 writeF32(os
, full_punch_interval
);
64 writeS16(os
, max_drop_level
);
65 writeU32(os
, groupcaps
.size());
66 for (const auto &groupcap
: groupcaps
) {
67 const std::string
*name
= &groupcap
.first
;
68 const ToolGroupCap
*cap
= &groupcap
.second
;
69 os
<< serializeString16(*name
);
70 writeS16(os
, cap
->uses
);
71 writeS16(os
, cap
->maxlevel
);
72 writeU32(os
, cap
->times
.size());
73 for (const auto &time
: cap
->times
) {
74 writeS16(os
, time
.first
);
75 writeF32(os
, time
.second
);
79 writeU32(os
, damageGroups
.size());
81 for (const auto &damageGroup
: damageGroups
) {
82 os
<< serializeString16(damageGroup
.first
);
83 writeS16(os
, damageGroup
.second
);
86 if (protocol_version
>= 38)
87 writeU16(os
, rangelim(punch_attack_uses
, 0, U16_MAX
));
90 void ToolCapabilities::deSerialize(std::istream
&is
)
92 int version
= readU8(is
);
94 throw SerializationError("unsupported ToolCapabilities version");
96 full_punch_interval
= readF32(is
);
97 max_drop_level
= readS16(is
);
99 u32 groupcaps_size
= readU32(is
);
100 for (u32 i
= 0; i
< groupcaps_size
; i
++) {
101 std::string name
= deSerializeString16(is
);
103 cap
.uses
= readS16(is
);
104 cap
.maxlevel
= readS16(is
);
105 u32 times_size
= readU32(is
);
106 for(u32 i
= 0; i
< times_size
; i
++) {
107 int level
= readS16(is
);
108 float time
= readF32(is
);
109 cap
.times
[level
] = time
;
111 groupcaps
[name
] = cap
;
114 u32 damage_groups_size
= readU32(is
);
115 for (u32 i
= 0; i
< damage_groups_size
; i
++) {
116 std::string name
= deSerializeString16(is
);
117 s16 rating
= readS16(is
);
118 damageGroups
[name
] = rating
;
122 punch_attack_uses
= readU16(is
);
125 void ToolCapabilities::serializeJson(std::ostream
&os
) const
128 root
["full_punch_interval"] = full_punch_interval
;
129 root
["max_drop_level"] = max_drop_level
;
130 root
["punch_attack_uses"] = punch_attack_uses
;
132 Json::Value groupcaps_object
;
133 for (const auto &groupcap
: groupcaps
) {
134 groupcap
.second
.toJson(groupcaps_object
[groupcap
.first
]);
136 root
["groupcaps"] = groupcaps_object
;
138 Json::Value damage_groups_object
;
139 DamageGroup::const_iterator dgiter
;
140 for (dgiter
= damageGroups
.begin(); dgiter
!= damageGroups
.end(); ++dgiter
) {
141 damage_groups_object
[dgiter
->first
] = dgiter
->second
;
143 root
["damage_groups"] = damage_groups_object
;
148 void ToolCapabilities::deserializeJson(std::istream
&is
)
152 if (root
.isObject()) {
153 if (root
["full_punch_interval"].isDouble())
154 full_punch_interval
= root
["full_punch_interval"].asFloat();
155 if (root
["max_drop_level"].isInt())
156 max_drop_level
= root
["max_drop_level"].asInt();
157 if (root
["punch_attack_uses"].isInt())
158 punch_attack_uses
= root
["punch_attack_uses"].asInt();
160 Json::Value
&groupcaps_object
= root
["groupcaps"];
161 if (groupcaps_object
.isObject()) {
162 Json::ValueIterator gciter
;
163 for (gciter
= groupcaps_object
.begin();
164 gciter
!= groupcaps_object
.end(); ++gciter
) {
165 ToolGroupCap groupcap
;
166 groupcap
.fromJson(*gciter
);
167 groupcaps
[gciter
.key().asString()] = groupcap
;
171 Json::Value
&damage_groups_object
= root
["damage_groups"];
172 if (damage_groups_object
.isObject()) {
173 Json::ValueIterator dgiter
;
174 for (dgiter
= damage_groups_object
.begin();
175 dgiter
!= damage_groups_object
.end(); ++dgiter
) {
176 Json::Value
&value
= *dgiter
;
178 damageGroups
[dgiter
.key().asString()] =
185 DigParams
getDigParams(const ItemGroupList
&groups
,
186 const ToolCapabilities
*tp
)
188 // Group dig_immediate defaults to fixed time and no wear
189 if (tp
->groupcaps
.find("dig_immediate") == tp
->groupcaps
.cend()) {
190 switch (itemgroup_get(groups
, "dig_immediate")) {
192 return DigParams(true, 0.5, 0, "dig_immediate");
194 return DigParams(true, 0, 0, "dig_immediate");
200 // Values to be returned (with a bit of conversion)
201 bool result_diggable
= false;
202 float result_time
= 0.0;
203 float result_wear
= 0.0;
204 std::string result_main_group
;
206 int level
= itemgroup_get(groups
, "level");
207 for (const auto &groupcap
: tp
->groupcaps
) {
208 const ToolGroupCap
&cap
= groupcap
.second
;
210 int leveldiff
= cap
.maxlevel
- level
;
214 const std::string
&groupname
= groupcap
.first
;
216 int rating
= itemgroup_get(groups
, groupname
);
217 bool time_exists
= cap
.getTime(rating
, &time
);
223 if (!result_diggable
|| time
< result_time
) {
225 result_diggable
= true;
227 result_wear
= 1.0 / cap
.uses
/ pow(3.0, leveldiff
);
230 result_main_group
= groupname
;
234 u16 wear_i
= U16_MAX
* result_wear
;
235 return DigParams(result_diggable
, result_time
, wear_i
, result_main_group
);
238 HitParams
getHitParams(const ItemGroupList
&armor_groups
,
239 const ToolCapabilities
*tp
, float time_from_last_punch
)
242 float result_wear
= 0.0f
;
243 float punch_interval_multiplier
=
244 rangelim(time_from_last_punch
/ tp
->full_punch_interval
, 0.0f
, 1.0f
);
246 for (const auto &damageGroup
: tp
->damageGroups
) {
247 s16 armor
= itemgroup_get(armor_groups
, damageGroup
.first
);
248 damage
+= damageGroup
.second
* punch_interval_multiplier
* armor
/ 100.0;
251 if (tp
->punch_attack_uses
> 0)
252 result_wear
= 1.0f
/ tp
->punch_attack_uses
* punch_interval_multiplier
;
254 u16 wear_i
= U16_MAX
* result_wear
;
255 return {damage
, wear_i
};
258 HitParams
getHitParams(const ItemGroupList
&armor_groups
,
259 const ToolCapabilities
*tp
)
261 return getHitParams(armor_groups
, tp
, 1000000);
264 PunchDamageResult
getPunchDamage(
265 const ItemGroupList
&armor_groups
,
266 const ToolCapabilities
*toolcap
,
267 const ItemStack
*punchitem
,
268 float time_from_last_punch
272 if (do_hit
&& punchitem
) {
273 if (itemgroup_get(armor_groups
, "punch_operable") &&
274 (toolcap
== NULL
|| punchitem
->name
.empty()))
279 if(itemgroup_get(armor_groups
, "immortal"))
284 PunchDamageResult result
;
287 HitParams hitparams
= getHitParams(armor_groups
, toolcap
,
288 time_from_last_punch
);
289 result
.did_punch
= true;
290 result
.wear
= hitparams
.wear
;
291 result
.damage
= hitparams
.hp
;
297 f32
getToolRange(const ItemDefinition
&def_selected
, const ItemDefinition
&def_hand
)
299 float max_d
= def_selected
.range
;
300 float max_d_hand
= def_hand
.range
;
302 if (max_d
< 0 && max_d_hand
>= 0)