From a637107a4e81be88938d68df4deae50e68cf2cd8 Mon Sep 17 00:00:00 2001 From: raymoo Date: Tue, 18 Apr 2017 16:30:27 -0700 Subject: [PATCH] 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. --- doc/lua_api.txt | 3 + src/game.cpp | 15 +++-- src/inventory.h | 15 +++-- src/itemstackmetadata.cpp | 41 +++++++++++++ src/itemstackmetadata.h | 22 +++++++ src/network/serverpackethandler.cpp | 7 ++- src/script/lua_api/l_itemstackmeta.cpp | 15 +++++ src/script/lua_api/l_itemstackmeta.h | 11 ++++ src/tool.cpp | 85 ++++++++++++++++++++++++++ src/tool.h | 6 ++ 10 files changed, 206 insertions(+), 14 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6109f082b..de4cc5029 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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. diff --git a/src/game.cpp b/src/game.cpp index cd4650baf..fd3914356 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -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); @@ -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); diff --git a/src/inventory.h b/src/inventory.h index d04dc1e69..465aa66db 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -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) diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index f63671425..53c8bad83 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -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; @@ -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, ""); } diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h index 843ef4832..0e1977c8c 100644 --- a/src/itemstackmetadata.h +++ b/src/itemstackmetadata.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "metadata.h" +#include "tool.h" class Inventory; class IItemDefManager; @@ -27,6 +28,27 @@ 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; }; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 31bff7b5c..b248b867b 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -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); } diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index c37a82116..3cbc3d0f7 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -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) { @@ -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} }; diff --git a/src/script/lua_api/l_itemstackmeta.h b/src/script/lua_api/l_itemstackmeta.h index 241f04c4c..46c40ab30 100644 --- a/src/script/lua_api/l_itemstackmeta.h +++ b/src/script/lua_api/l_itemstackmeta.h @@ -40,7 +40,18 @@ private: 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); diff --git a/src/tool.cpp b/src/tool.cpp index 839b1e387..2d2f9dee5 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -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 ×_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 @@ -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) { diff --git a/src/tool.h b/src/tool.h index c6bf0ad73..f6b196a49 100644 --- a/src/tool.h +++ b/src/tool.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "itemgroup.h" +#include struct ToolGroupCap { @@ -42,6 +43,9 @@ struct ToolGroupCap *time = i->second; return true; } + + void toJson(Json::Value &object) const; + void fromJson(const Json::Value &json); }; @@ -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 -- 2.25.1