Skip to content

Commit a637107

Browse files
raymooparamat
authored andcommittedOct 29, 2017
Allow overriding tool capabilities through itemstack metadata
This makes it possible to modify the tool capabilities of individual itemstacks by calling a method on itemstack metadata references.
1 parent 610ea6f commit a637107

10 files changed

+206
-14
lines changed
 

Diff for: ‎doc/lua_api.txt

+3
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,9 @@ Can be obtained via `item:get_meta()`.
33663366

33673367
#### Methods
33683368
* All methods in MetaDataRef
3369+
* `set_tool_capabilities([tool_capabilities])`
3370+
* overrides the item's tool capabilities
3371+
* a nil value will clear the override data and restore the original behavior
33693372

33703373
### `StorageRef`
33713374
Mod metadata: per mod metadata, saved automatically.

Diff for: ‎src/game.cpp

+10-5
Original file line numberDiff line numberDiff line change
@@ -3701,8 +3701,13 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
37013701
} else if (pointed.type == POINTEDTHING_NODE) {
37023702
ToolCapabilities playeritem_toolcap =
37033703
playeritem.getToolCapabilities(itemdef_manager);
3704-
if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) {
3705-
playeritem_toolcap = *hand_def.tool_capabilities;
3704+
if (playeritem.name.empty()) {
3705+
const ToolCapabilities *handToolcap = hlist
3706+
? &hlist->getItem(0).getToolCapabilities(itemdef_manager)
3707+
: itemdef_manager->get("").tool_capabilities;
3708+
3709+
if (handToolcap != nullptr)
3710+
playeritem_toolcap = *handToolcap;
37063711
}
37073712
handlePointingAtNode(pointed, playeritem_def, playeritem,
37083713
playeritem_toolcap, dtime);
@@ -4004,9 +4009,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
40044009
// If can't dig, try hand
40054010
if (!params.diggable) {
40064011
InventoryList *hlist = local_inventory->getList("hand");
4007-
const ItemDefinition &hand =
4008-
hlist ? hlist->getItem(0).getDefinition(itemdef_manager) : itemdef_manager->get("");
4009-
const ToolCapabilities *tp = hand.tool_capabilities;
4012+
const ToolCapabilities *tp = hlist
4013+
? &hlist->getItem(0).getToolCapabilities(itemdef_manager)
4014+
: itemdef_manager->get("").tool_capabilities;
40104015

40114016
if (tp)
40124017
params = getDigParams(nodedef_manager->get(n).groups, tp);

Diff for: ‎src/inventory.h

+9-6
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,15 @@ struct ItemStack
111111
const ToolCapabilities& getToolCapabilities(
112112
IItemDefManager *itemdef) const
113113
{
114-
ToolCapabilities *cap;
115-
cap = itemdef->get(name).tool_capabilities;
116-
if(cap == NULL)
117-
cap = itemdef->get("").tool_capabilities;
118-
assert(cap != NULL);
119-
return *cap;
114+
const ToolCapabilities *item_cap =
115+
itemdef->get(name).tool_capabilities;
116+
117+
if (item_cap == NULL)
118+
// Fall back to the hand's tool capabilities
119+
item_cap = itemdef->get("").tool_capabilities;
120+
121+
assert(item_cap != NULL);
122+
return metadata.getToolCapabilities(*item_cap); // Check for override
120123
}
121124

122125
// Wear out (only tools)

Diff for: ‎src/itemstackmetadata.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@
99
#define DESERIALIZE_KV_DELIM_STR "\x02"
1010
#define DESERIALIZE_PAIR_DELIM_STR "\x03"
1111

12+
#define TOOLCAP_KEY "tool_capabilities"
13+
14+
void ItemStackMetadata::clear()
15+
{
16+
Metadata::clear();
17+
updateToolCapabilities();
18+
}
19+
20+
bool ItemStackMetadata::setString(const std::string &name, const std::string &var)
21+
{
22+
bool result = Metadata::setString(name, var);
23+
if (name == TOOLCAP_KEY)
24+
updateToolCapabilities();
25+
return result;
26+
}
27+
1228
void ItemStackMetadata::serialize(std::ostream &os) const
1329
{
1430
std::ostringstream os2;
@@ -41,4 +57,29 @@ void ItemStackMetadata::deSerialize(std::istream &is)
4157
m_stringvars[""] = in;
4258
}
4359
}
60+
updateToolCapabilities();
61+
}
62+
63+
void ItemStackMetadata::updateToolCapabilities()
64+
{
65+
if (contains(TOOLCAP_KEY)) {
66+
toolcaps_overridden = true;
67+
toolcaps_override = ToolCapabilities();
68+
std::istringstream is(getString(TOOLCAP_KEY));
69+
toolcaps_override.deserializeJson(is);
70+
} else {
71+
toolcaps_overridden = false;
72+
}
73+
}
74+
75+
void ItemStackMetadata::setToolCapabilities(const ToolCapabilities &caps)
76+
{
77+
std::ostringstream os;
78+
caps.serializeJson(os);
79+
setString(TOOLCAP_KEY, os.str());
80+
}
81+
82+
void ItemStackMetadata::clearToolCapabilities()
83+
{
84+
setString(TOOLCAP_KEY, "");
4485
}

Diff for: ‎src/itemstackmetadata.h

+22
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2020
#pragma once
2121

2222
#include "metadata.h"
23+
#include "tool.h"
2324

2425
class Inventory;
2526
class IItemDefManager;
2627

2728
class ItemStackMetadata : public Metadata
2829
{
2930
public:
31+
ItemStackMetadata() : toolcaps_overridden(false) {}
32+
33+
// Overrides
34+
void clear() override;
35+
bool setString(const std::string &name, const std::string &var) override;
36+
3037
void serialize(std::ostream &os) const;
3138
void deSerialize(std::istream &is);
39+
40+
const ToolCapabilities &getToolCapabilities(
41+
const ToolCapabilities &default_caps) const
42+
{
43+
return toolcaps_overridden ? toolcaps_override : default_caps;
44+
}
45+
46+
void setToolCapabilities(const ToolCapabilities &caps);
47+
void clearToolCapabilities();
48+
49+
private:
50+
void updateToolCapabilities();
51+
52+
bool toolcaps_overridden;
53+
ToolCapabilities toolcaps_override;
3254
};

Diff for: ‎src/network/serverpackethandler.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -1210,9 +1210,10 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
12101210
// If can't dig, try hand
12111211
if (!params.diggable) {
12121212
InventoryList *hlist = playersao->getInventory()->getList("hand");
1213-
const ItemDefinition &hand =
1214-
hlist ? hlist->getItem(0).getDefinition(m_itemdef) : m_itemdef->get("");
1215-
const ToolCapabilities *tp = hand.tool_capabilities;
1213+
const ToolCapabilities *tp = hlist
1214+
? &hlist->getItem(0).getToolCapabilities(m_itemdef)
1215+
: m_itemdef->get("").tool_capabilities;
1216+
12161217
if (tp)
12171218
params = getDigParams(m_nodedef->get(n).groups, tp);
12181219
}

Diff for: ‎src/script/lua_api/l_itemstackmeta.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ void ItemStackMetaRef::reportMetadataChange()
5050
}
5151

5252
// Exported functions
53+
int ItemStackMetaRef::l_set_tool_capabilities(lua_State *L)
54+
{
55+
ItemStackMetaRef *metaref = checkobject(L, 1);
56+
if (lua_isnoneornil(L, 2)) {
57+
metaref->clearToolCapabilities();
58+
} else if (lua_istable(L, 2)) {
59+
ToolCapabilities caps = read_tool_capabilities(L, 2);
60+
metaref->setToolCapabilities(caps);
61+
} else {
62+
luaL_typerror(L, 2, "table or nil");
63+
}
64+
65+
return 0;
66+
}
5367

5468
// garbage collector
5569
int ItemStackMetaRef::gc_object(lua_State *L) {
@@ -116,5 +130,6 @@ const luaL_Reg ItemStackMetaRef::methods[] = {
116130
luamethod(MetaDataRef, to_table),
117131
luamethod(MetaDataRef, from_table),
118132
luamethod(MetaDataRef, equals),
133+
luamethod(ItemStackMetaRef, set_tool_capabilities),
119134
{0,0}
120135
};

Diff for: ‎src/script/lua_api/l_itemstackmeta.h

+11
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,18 @@ class ItemStackMetaRef : public MetaDataRef
4040

4141
virtual void reportMetadataChange();
4242

43+
void setToolCapabilities(const ToolCapabilities &caps)
44+
{
45+
istack->metadata.setToolCapabilities(caps);
46+
}
47+
48+
void clearToolCapabilities()
49+
{
50+
istack->metadata.clearToolCapabilities();
51+
}
52+
4353
// Exported functions
54+
static int l_set_tool_capabilities(lua_State *L);
4455

4556
// garbage collector
4657
static int gc_object(lua_State *L);

Diff for: ‎src/tool.cpp

+85
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2525
#include "util/serialize.h"
2626
#include "util/numeric.h"
2727

28+
void ToolGroupCap::toJson(Json::Value &object) const
29+
{
30+
object["maxlevel"] = maxlevel;
31+
object["uses"] = uses;
32+
33+
Json::Value times_object;
34+
for (auto time : times)
35+
times_object[time.first] = time.second;
36+
object["times"] = times_object;
37+
}
38+
39+
void ToolGroupCap::fromJson(const Json::Value &json)
40+
{
41+
if (json.isObject()) {
42+
if (json["maxlevel"].isInt())
43+
maxlevel = json["maxlevel"].asInt();
44+
if (json["uses"].isInt())
45+
uses = json["uses"].asInt();
46+
const Json::Value &times_object = json["times"];
47+
if (times_object.isArray()) {
48+
Json::ArrayIndex size = times_object.size();
49+
for (Json::ArrayIndex i = 0; i < size; ++i)
50+
if (times_object[i].isDouble())
51+
times[i] = times_object[i].asFloat();
52+
}
53+
}
54+
}
55+
2856
void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
2957
{
3058
writeU8(os, 3); // protocol_version >= 36
@@ -84,6 +112,63 @@ void ToolCapabilities::deSerialize(std::istream &is)
84112
}
85113
}
86114

115+
void ToolCapabilities::serializeJson(std::ostream &os) const
116+
{
117+
Json::Value root;
118+
root["full_punch_interval"] = full_punch_interval;
119+
root["max_drop_level"] = max_drop_level;
120+
121+
Json::Value groupcaps_object;
122+
for (auto groupcap : groupcaps) {
123+
groupcap.second.toJson(groupcaps_object[groupcap.first]);
124+
}
125+
root["groupcaps"] = groupcaps_object;
126+
127+
Json::Value damage_groups_object;
128+
DamageGroup::const_iterator dgiter;
129+
for (dgiter = damageGroups.begin(); dgiter != damageGroups.end(); ++dgiter) {
130+
damage_groups_object[dgiter->first] = dgiter->second;
131+
}
132+
root["damage_groups"] = damage_groups_object;
133+
134+
os << root;
135+
}
136+
137+
void ToolCapabilities::deserializeJson(std::istream &is)
138+
{
139+
Json::Value root;
140+
is >> root;
141+
if (root.isObject()) {
142+
if (root["full_punch_interval"].isDouble())
143+
full_punch_interval = root["full_punch_interval"].asFloat();
144+
if (root["max_drop_level"].isInt())
145+
max_drop_level = root["max_drop_level"].asInt();
146+
147+
Json::Value &groupcaps_object = root["groupcaps"];
148+
if (groupcaps_object.isObject()) {
149+
Json::ValueIterator gciter;
150+
for (gciter = groupcaps_object.begin();
151+
gciter != groupcaps_object.end(); ++gciter) {
152+
ToolGroupCap groupcap;
153+
groupcap.fromJson(*gciter);
154+
groupcaps[gciter.key().asString()] = groupcap;
155+
}
156+
}
157+
158+
Json::Value &damage_groups_object = root["damage_groups"];
159+
if (damage_groups_object.isObject()) {
160+
Json::ValueIterator dgiter;
161+
for (dgiter = damage_groups_object.begin();
162+
dgiter != damage_groups_object.end(); ++dgiter) {
163+
Json::Value &value = *dgiter;
164+
if (value.isInt())
165+
damageGroups[dgiter.key().asString()] =
166+
value.asInt();
167+
}
168+
}
169+
}
170+
}
171+
87172
DigParams getDigParams(const ItemGroupList &groups,
88173
const ToolCapabilities *tp, float time_from_last_punch)
89174
{

Diff for: ‎src/tool.h

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2323
#include <string>
2424
#include <iostream>
2525
#include "itemgroup.h"
26+
#include <json/json.h>
2627

2728
struct ToolGroupCap
2829
{
@@ -42,6 +43,9 @@ struct ToolGroupCap
4243
*time = i->second;
4344
return true;
4445
}
46+
47+
void toJson(Json::Value &object) const;
48+
void fromJson(const Json::Value &json);
4549
};
4650

4751

@@ -69,6 +73,8 @@ struct ToolCapabilities
6973

7074
void serialize(std::ostream &os, u16 version) const;
7175
void deSerialize(std::istream &is);
76+
void serializeJson(std::ostream &os) const;
77+
void deserializeJson(std::istream &is);
7278
};
7379

7480
struct DigParams

3 commit comments

Comments
 (3)

pandaro commented on Oct 30, 2017

@pandaro
Contributor

+1 love

pandaro commented on Feb 3, 2018

@pandaro
Contributor

I'm working on a mod that use this function: "set_tool_capabilities()" but i'm not able to understand it at all:
all work fine if i use it inside a tool definition like this on my repository.
but outside a tool definition, for example in a minetest.on_punch_node() or inside a globalstep(), not work for me.
So, can you give explanation about correct use of this new function and eventually limits?

@raymoo

raymoo commented on Feb 4, 2018

@raymoo
ContributorAuthor

@pandaro
In your globalstep callback you are setting the player's wielded item to a new item with nothing overridden.

As for the punchnode callback, the return value is ignored. You have to set the item manually (probably using set_wielded_item like you tried to do in the globalstep callback).

Also on line 9 you left in some comment that I am guessing came from copy and pasting.

Please sign in to comment.