Skip to content

Commit

Permalink
Allow overriding tool capabilities through itemstack metadata
Browse files Browse the repository at this point in the history
This makes it possible to modify the tool capabilities of individual
itemstacks by calling a method on itemstack metadata references.
  • Loading branch information
raymoo authored and paramat committed Oct 29, 2017
1 parent 610ea6f commit a637107
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 14 deletions.
3 changes: 3 additions & 0 deletions doc/lua_api.txt
Expand Up @@ -3366,6 +3366,9 @@ Can be obtained via `item:get_meta()`.

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

### `StorageRef`
Mod metadata: per mod metadata, saved automatically.
Expand Down
15 changes: 10 additions & 5 deletions src/game.cpp
Expand Up @@ -3701,8 +3701,13 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
} else if (pointed.type == POINTEDTHING_NODE) {
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef_manager);
if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) {
playeritem_toolcap = *hand_def.tool_capabilities;
if (playeritem.name.empty()) {
const ToolCapabilities *handToolcap = hlist
? &hlist->getItem(0).getToolCapabilities(itemdef_manager)
: itemdef_manager->get("").tool_capabilities;

if (handToolcap != nullptr)
playeritem_toolcap = *handToolcap;
}
handlePointingAtNode(pointed, playeritem_def, playeritem,
playeritem_toolcap, dtime);
Expand Down Expand Up @@ -4004,9 +4009,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
// If can't dig, try hand
if (!params.diggable) {
InventoryList *hlist = local_inventory->getList("hand");
const ItemDefinition &hand =
hlist ? hlist->getItem(0).getDefinition(itemdef_manager) : itemdef_manager->get("");
const ToolCapabilities *tp = hand.tool_capabilities;
const ToolCapabilities *tp = hlist
? &hlist->getItem(0).getToolCapabilities(itemdef_manager)
: itemdef_manager->get("").tool_capabilities;

if (tp)
params = getDigParams(nodedef_manager->get(n).groups, tp);
Expand Down
15 changes: 9 additions & 6 deletions src/inventory.h
Expand Up @@ -111,12 +111,15 @@ struct ItemStack
const ToolCapabilities& getToolCapabilities(
IItemDefManager *itemdef) const
{
ToolCapabilities *cap;
cap = itemdef->get(name).tool_capabilities;
if(cap == NULL)
cap = itemdef->get("").tool_capabilities;
assert(cap != NULL);
return *cap;
const ToolCapabilities *item_cap =
itemdef->get(name).tool_capabilities;

if (item_cap == NULL)
// Fall back to the hand's tool capabilities
item_cap = itemdef->get("").tool_capabilities;

assert(item_cap != NULL);
return metadata.getToolCapabilities(*item_cap); // Check for override
}

// Wear out (only tools)
Expand Down
41 changes: 41 additions & 0 deletions src/itemstackmetadata.cpp
Expand Up @@ -9,6 +9,22 @@
#define DESERIALIZE_KV_DELIM_STR "\x02"
#define DESERIALIZE_PAIR_DELIM_STR "\x03"

#define TOOLCAP_KEY "tool_capabilities"

void ItemStackMetadata::clear()
{
Metadata::clear();
updateToolCapabilities();
}

bool ItemStackMetadata::setString(const std::string &name, const std::string &var)
{
bool result = Metadata::setString(name, var);
if (name == TOOLCAP_KEY)
updateToolCapabilities();
return result;
}

void ItemStackMetadata::serialize(std::ostream &os) const
{
std::ostringstream os2;
Expand Down Expand Up @@ -41,4 +57,29 @@ void ItemStackMetadata::deSerialize(std::istream &is)
m_stringvars[""] = in;
}
}
updateToolCapabilities();
}

void ItemStackMetadata::updateToolCapabilities()
{
if (contains(TOOLCAP_KEY)) {
toolcaps_overridden = true;
toolcaps_override = ToolCapabilities();
std::istringstream is(getString(TOOLCAP_KEY));
toolcaps_override.deserializeJson(is);
} else {
toolcaps_overridden = false;
}
}

void ItemStackMetadata::setToolCapabilities(const ToolCapabilities &caps)
{
std::ostringstream os;
caps.serializeJson(os);
setString(TOOLCAP_KEY, os.str());
}

void ItemStackMetadata::clearToolCapabilities()
{
setString(TOOLCAP_KEY, "");
}
22 changes: 22 additions & 0 deletions src/itemstackmetadata.h
Expand Up @@ -20,13 +20,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once

#include "metadata.h"
#include "tool.h"

class Inventory;
class IItemDefManager;

class ItemStackMetadata : public Metadata
{
public:
ItemStackMetadata() : toolcaps_overridden(false) {}

// Overrides
void clear() override;
bool setString(const std::string &name, const std::string &var) override;

void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);

const ToolCapabilities &getToolCapabilities(
const ToolCapabilities &default_caps) const
{
return toolcaps_overridden ? toolcaps_override : default_caps;
}

void setToolCapabilities(const ToolCapabilities &caps);
void clearToolCapabilities();

private:
void updateToolCapabilities();

bool toolcaps_overridden;
ToolCapabilities toolcaps_override;
};
7 changes: 4 additions & 3 deletions src/network/serverpackethandler.cpp
Expand Up @@ -1210,9 +1210,10 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
// If can't dig, try hand
if (!params.diggable) {
InventoryList *hlist = playersao->getInventory()->getList("hand");
const ItemDefinition &hand =
hlist ? hlist->getItem(0).getDefinition(m_itemdef) : m_itemdef->get("");
const ToolCapabilities *tp = hand.tool_capabilities;
const ToolCapabilities *tp = hlist
? &hlist->getItem(0).getToolCapabilities(m_itemdef)
: m_itemdef->get("").tool_capabilities;

if (tp)
params = getDigParams(m_nodedef->get(n).groups, tp);
}
Expand Down
15 changes: 15 additions & 0 deletions src/script/lua_api/l_itemstackmeta.cpp
Expand Up @@ -50,6 +50,20 @@ void ItemStackMetaRef::reportMetadataChange()
}

// Exported functions
int ItemStackMetaRef::l_set_tool_capabilities(lua_State *L)
{
ItemStackMetaRef *metaref = checkobject(L, 1);
if (lua_isnoneornil(L, 2)) {
metaref->clearToolCapabilities();
} else if (lua_istable(L, 2)) {
ToolCapabilities caps = read_tool_capabilities(L, 2);
metaref->setToolCapabilities(caps);
} else {
luaL_typerror(L, 2, "table or nil");
}

return 0;
}

// garbage collector
int ItemStackMetaRef::gc_object(lua_State *L) {
Expand Down Expand Up @@ -116,5 +130,6 @@ const luaL_Reg ItemStackMetaRef::methods[] = {
luamethod(MetaDataRef, to_table),
luamethod(MetaDataRef, from_table),
luamethod(MetaDataRef, equals),
luamethod(ItemStackMetaRef, set_tool_capabilities),
{0,0}
};
11 changes: 11 additions & 0 deletions src/script/lua_api/l_itemstackmeta.h
Expand Up @@ -40,7 +40,18 @@ class ItemStackMetaRef : public MetaDataRef

virtual void reportMetadataChange();

void setToolCapabilities(const ToolCapabilities &caps)
{
istack->metadata.setToolCapabilities(caps);
}

void clearToolCapabilities()
{
istack->metadata.clearToolCapabilities();
}

// Exported functions
static int l_set_tool_capabilities(lua_State *L);

// garbage collector
static int gc_object(lua_State *L);
Expand Down
85 changes: 85 additions & 0 deletions src/tool.cpp
Expand Up @@ -25,6 +25,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#include "util/numeric.h"

void ToolGroupCap::toJson(Json::Value &object) const
{
object["maxlevel"] = maxlevel;
object["uses"] = uses;

Json::Value times_object;
for (auto time : times)
times_object[time.first] = time.second;
object["times"] = times_object;
}

void ToolGroupCap::fromJson(const Json::Value &json)
{
if (json.isObject()) {
if (json["maxlevel"].isInt())
maxlevel = json["maxlevel"].asInt();
if (json["uses"].isInt())
uses = json["uses"].asInt();
const Json::Value &times_object = json["times"];
if (times_object.isArray()) {
Json::ArrayIndex size = times_object.size();
for (Json::ArrayIndex i = 0; i < size; ++i)
if (times_object[i].isDouble())
times[i] = times_object[i].asFloat();
}
}
}

void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
{
writeU8(os, 3); // protocol_version >= 36
Expand Down Expand Up @@ -84,6 +112,63 @@ void ToolCapabilities::deSerialize(std::istream &is)
}
}

void ToolCapabilities::serializeJson(std::ostream &os) const
{
Json::Value root;
root["full_punch_interval"] = full_punch_interval;
root["max_drop_level"] = max_drop_level;

Json::Value groupcaps_object;
for (auto groupcap : groupcaps) {
groupcap.second.toJson(groupcaps_object[groupcap.first]);
}
root["groupcaps"] = groupcaps_object;

Json::Value damage_groups_object;
DamageGroup::const_iterator dgiter;
for (dgiter = damageGroups.begin(); dgiter != damageGroups.end(); ++dgiter) {
damage_groups_object[dgiter->first] = dgiter->second;
}
root["damage_groups"] = damage_groups_object;

os << root;
}

void ToolCapabilities::deserializeJson(std::istream &is)
{
Json::Value root;
is >> root;
if (root.isObject()) {
if (root["full_punch_interval"].isDouble())
full_punch_interval = root["full_punch_interval"].asFloat();
if (root["max_drop_level"].isInt())
max_drop_level = root["max_drop_level"].asInt();

Json::Value &groupcaps_object = root["groupcaps"];
if (groupcaps_object.isObject()) {
Json::ValueIterator gciter;
for (gciter = groupcaps_object.begin();
gciter != groupcaps_object.end(); ++gciter) {
ToolGroupCap groupcap;
groupcap.fromJson(*gciter);
groupcaps[gciter.key().asString()] = groupcap;
}
}

Json::Value &damage_groups_object = root["damage_groups"];
if (damage_groups_object.isObject()) {
Json::ValueIterator dgiter;
for (dgiter = damage_groups_object.begin();
dgiter != damage_groups_object.end(); ++dgiter) {
Json::Value &value = *dgiter;
if (value.isInt())
damageGroups[dgiter.key().asString()] =
value.asInt();
}
}
}
}

DigParams getDigParams(const ItemGroupList &groups,
const ToolCapabilities *tp, float time_from_last_punch)
{
Expand Down
6 changes: 6 additions & 0 deletions src/tool.h
Expand Up @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <iostream>
#include "itemgroup.h"
#include <json/json.h>

struct ToolGroupCap
{
Expand All @@ -42,6 +43,9 @@ struct ToolGroupCap
*time = i->second;
return true;
}

void toJson(Json::Value &object) const;
void fromJson(const Json::Value &json);
};


Expand Down Expand Up @@ -69,6 +73,8 @@ struct ToolCapabilities

void serialize(std::ostream &os, u16 version) const;
void deSerialize(std::istream &is);
void serializeJson(std::ostream &os) const;
void deserializeJson(std::istream &is);
};

struct DigParams
Expand Down

3 comments on commit a637107

@pandaro
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 love

@pandaro
Copy link
Contributor

@pandaro pandaro commented on a637107 Feb 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor Author

@raymoo raymoo commented on a637107 Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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