From 2da40ca47d07580008965821e9e368d50b63f714 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 23 Aug 2023 13:38:22 -0300 Subject: [PATCH] improve: rework in unscripted weapons and moveevents Add moveevent and weapons unscripted to load in items.xml TODO: - [ ] Fix reload items crash - [ ] Implement load weapons logic --- .../equipment/unscripted_equipments.lua | 22 --- data/items/items.xml | 5 + src/canary_server.cpp | 9 +- src/items/functions/item/item_parse.cpp | 126 ++++++++++++++++++ src/items/functions/item/item_parse.hpp | 3 + src/items/items_definitions.hpp | 1 + src/lua/creature/movement.hpp | 9 +- src/utils/tools.cpp | 4 +- src/utils/tools.hpp | 2 +- 9 files changed, 151 insertions(+), 30 deletions(-) diff --git a/data-otservbr-global/scripts/movements/equipment/unscripted_equipments.lua b/data-otservbr-global/scripts/movements/equipment/unscripted_equipments.lua index 7361ad8cd98..6c4ed244b59 100644 --- a/data-otservbr-global/scripts/movements/equipment/unscripted_equipments.lua +++ b/data-otservbr-global/scripts/movements/equipment/unscripted_equipments.lua @@ -241,28 +241,6 @@ local items = { {"Royal Paladin"} } }, - { - -- sanguine legs - itemid = 43876, - type = "equip", - slot = "legs", - level = 500, - vocation = { - {"Knight", true}, - {"Elite Knight"} - } - }, - { - -- sanguine legs - itemid = 43876, - type = "deequip", - slot = "legs", - level = 500, - vocation = { - {"Knight", true}, - {"Elite Knight"} - } - }, { -- grand sanguine battleaxe itemid = 43875, diff --git a/data/items/items.xml b/data/items/items.xml index d6d5e0982a9..f7c5c1302ab 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -59248,6 +59248,11 @@ + + + + + diff --git a/src/canary_server.cpp b/src/canary_server.cpp index fff0cc4d9a3..64d5a0377a2 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -323,6 +323,11 @@ void CanaryServer::loadModules() { auto coreFolder = g_configManager().getString(CORE_DIRECTORY); // Load items dependencies modulesLoadHelper((g_game().loadAppearanceProtobuf(coreFolder + "/items/appearances.dat") == ERROR_NONE), "appearances.dat"); + // Load XML core first + modulesLoadHelper(g_vocations().loadFromXml(), "XML/vocations.xml"); + modulesLoadHelper(Outfits::getInstance().loadFromXml(), "XML/outfits.xml"); + modulesLoadHelper(Familiars::getInstance().loadFromXml(), "XML/familiars.xml"); + modulesLoadHelper(g_storages().loadFromXML(), "XML/storages.xml"); modulesLoadHelper(Item::items.loadFromXml(), "items.xml"); auto datapackFolder = g_configManager().getString(DATA_DIRECTORY); @@ -332,12 +337,8 @@ void CanaryServer::loadModules() { modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts", false, false), "/data/scripts"); // Second XML scripts - modulesLoadHelper(g_vocations().loadFromXml(), "XML/vocations.xml"); modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromXml(), "XML/events.xml"); - modulesLoadHelper(Outfits::getInstance().loadFromXml(), "XML/outfits.xml"); - modulesLoadHelper(Familiars::getInstance().loadFromXml(), "XML/familiars.xml"); modulesLoadHelper(g_imbuements().loadFromXml(), "XML/imbuements.xml"); - modulesLoadHelper(g_storages().loadFromXML(), "XML/storages.xml"); modulesLoadHelper(g_modules().loadFromXml(), "modules/modules.xml"); modulesLoadHelper(g_events().loadFromXml(), "events/events.xml"); modulesLoadHelper((g_npcs().load(true, false)), "npclib"); diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index b3324f8dd73..931da29d01b 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -10,6 +10,7 @@ #include "pch.hpp" #include "items/functions/item/item_parse.hpp" +#include "lua/creature/movement.hpp" #include "utils/pugicast.hpp" void ItemParse::initParse(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { @@ -73,6 +74,7 @@ void ItemParse::initParse(const std::string &tmpStrValue, pugi::xml_node attribu ItemParse::parseCleavePercent(tmpStrValue, valueAttribute, itemType); ItemParse::parseReflectDamage(tmpStrValue, valueAttribute, itemType); ItemParse::parseTransformOnUse(tmpStrValue, valueAttribute, itemType); + ItemParse::parseUnscriptedItems(tmpStrValue, attributeNode, valueAttribute, itemType); } void ItemParse::parseDummyRate(pugi::xml_node attributeNode, ItemType &itemType) { @@ -940,3 +942,127 @@ void ItemParse::parseTransformOnUse(const std::string_view &tmpStrValue, pugi::x itemType.m_transformOnUse = pugi::cast(valueAttribute.value()); } } + +void ItemParse::createAndRegisterMoveEvent(MoveEvent_t eventType, const ItemType &itemType, pugi::xml_node attributeNode) { + auto moveevent = std::make_shared(nullptr); + moveevent->setItemId(itemType.id); + moveevent->setEventType(eventType); + if (eventType == MOVE_EVENT_EQUIP) { + moveevent->equipFunction = moveevent->EquipItem; + } else if (eventType == MOVE_EVENT_DEEQUIP) { + moveevent->equipFunction = moveevent->DeEquipItem; + } + + for (auto subAttributeNode : attributeNode.children()) { + pugi::xml_attribute subKeyAttribute = subAttributeNode.attribute("key"); + if (!subKeyAttribute) { + continue; + } + + pugi::xml_attribute subValueAttribute = subAttributeNode.attribute("value"); + if (!subValueAttribute) { + continue; + } + + auto stringKey = asLowerCaseString(subKeyAttribute.as_string()); + if (stringKey == "slot") { + if (moveevent->getEventType() == MOVE_EVENT_EQUIP || moveevent->getEventType() == MOVE_EVENT_DEEQUIP) { + auto slotName = asLowerCaseString(subValueAttribute.as_string()); + if (slotName == "head") { + moveevent->setSlot(SLOTP_HEAD); + } else if (slotName == "necklace") { + moveevent->setSlot(SLOTP_NECKLACE); + } else if (slotName == "backpack") { + moveevent->setSlot(SLOTP_BACKPACK); + } else if (slotName == "armor" || slotName == "body") { + moveevent->setSlot(SLOTP_ARMOR); + } else if (slotName == "right-hand") { + moveevent->setSlot(SLOTP_RIGHT); + } else if (slotName == "left-hand") { + moveevent->setSlot(SLOTP_LEFT); + } else if (slotName == "hand" || slotName == "shield") { + moveevent->setSlot(SLOTP_RIGHT | SLOTP_LEFT); + } else if (slotName == "legs") { + moveevent->setSlot(SLOTP_LEGS); + } else if (slotName == "feet") { + moveevent->setSlot(SLOTP_FEET); + } else if (slotName == "ring") { + moveevent->setSlot(SLOTP_RING); + } else if (slotName == "ammo") { + moveevent->setSlot(SLOTP_AMMO); + } else { + g_logger().warn("[{}] unknown slot type: {}", __FUNCTION__, slotName); + } + } + } else if (stringKey == "level") { + auto numberValue = subValueAttribute.as_uint(); + moveevent->setRequiredLevel(numberValue); + moveevent->setWieldInfo(WIELDINFO_LEVEL); + } else if (stringKey == "vocation") { + auto vocations = subValueAttribute.as_string(); + std::string tmp; + std::stringstream ss(vocations); + std::string token; + + while (std::getline(ss, token, ',')) { + token.erase(token.begin(), std::find_if(token.begin(), token.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); + token.erase(std::find_if(token.rbegin(), token.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), + token.end()); + + std::string v1; + bool showInDescription = false; + + std::stringstream inner_ss(token); + std::getline(inner_ss, v1, ';'); + std::string showInDescriptionStr; + std::getline(inner_ss, showInDescriptionStr, ';'); + showInDescription = showInDescriptionStr == "true"; + + moveevent->addVocEquipMap(v1); + moveevent->setWieldInfo(WIELDINFO_VOCREQ); + + if (showInDescription) { + if (moveevent->getVocationString().empty()) { + tmp = asLowerCaseString(v1); + tmp += "s"; + moveevent->setVocationString(tmp); + } else { + tmp += ", "; + tmp += asLowerCaseString(v1); + tmp += "s"; + } + } + } + + size_t lastComma = tmp.rfind(','); + if (lastComma != std::string::npos) { + tmp.replace(lastComma, 1, " and"); + moveevent->setVocationString(tmp); + } + } + } + + if (!g_moveEvents().registerLuaItemEvent(moveevent)) { + g_logger().error("[{}] failed to register moveevent from item name {}", __FUNCTION__, itemType.name); + } +} + +void ItemParse::parseUnscriptedItems(const std::string_view &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { + if (tmpStrValue == "script") { + std::string scriptName = valueAttribute.as_string(); + auto tokens = split(scriptName.data(), ';'); + std::vector> events; + for (const auto &token : tokens) { + if (token == "moveevent") { + createAndRegisterMoveEvent(MOVE_EVENT_EQUIP, itemType, attributeNode); + createAndRegisterMoveEvent(MOVE_EVENT_DEEQUIP, itemType, attributeNode); + } else if ((token == "weapon")) { + // TODO: add weapon logic + } + } + } +} diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index 28a8e71fc53..01fb33172e5 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -155,6 +155,7 @@ const phmap::flat_hash_map ItemParseAttribut { "cleavepercent", ITEM_PARSE_CLEAVEPERCENT }, { "reflectdamage", ITEM_PARSE_REFLECTDAMAGE }, { "reflectpercentall", ITEM_PARSE_REFLECTPERCENTALL }, + { "script", ITEM_PARSE_SCRIPT }, }; const phmap::flat_hash_map ItemTypesMap = { @@ -308,10 +309,12 @@ class ItemParse : public Items { static void parseCleavePercent(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseReflectDamage(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseTransformOnUse(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parseUnscriptedItems(const std::string_view &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); private: // Parent of the function: static void parseField static std::tuple parseFieldConditions(std::string lowerStringValue, pugi::xml_attribute valueAttribute); static CombatType_t parseFieldCombatType(std::string string, pugi::xml_attribute valueAttribute); static void parseFieldCombatDamage(ConditionDamage* conditionDamage, std::string stringValue, pugi::xml_node attributeNode); + static void createAndRegisterMoveEvent(MoveEvent_t eventType, const ItemType &itemType, pugi::xml_node attributeNode); }; diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 85dee43bf1a..81f68e17d06 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -465,6 +465,7 @@ enum ItemParseAttributes_t { ITEM_PARSE_CLEAVEPERCENT, ITEM_PARSE_REFLECTPERCENTALL, ITEM_PARSE_REFLECTDAMAGE, + ITEM_PARSE_SCRIPT, }; struct ImbuementInfo { diff --git a/src/lua/creature/movement.hpp b/src/lua/creature/movement.hpp index dd8d79e99f7..214e73aaf69 100644 --- a/src/lua/creature/movement.hpp +++ b/src/lua/creature/movement.hpp @@ -179,7 +179,7 @@ class MoveEvent final : public Script, public SharedObject { return vocEquipMap; } void addVocEquipMap(std::string vocName) { - int32_t vocationId = g_vocations().getVocationId(vocName); + uint16_t vocationId = g_vocations().getVocationId(vocName); if (vocationId != -1) { vocEquipMap[vocationId] = true; } @@ -251,6 +251,10 @@ class MoveEvent final : public Script, public SharedObject { static uint32_t EquipItem(const std::shared_ptr &moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); static uint32_t DeEquipItem(const std::shared_ptr &moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); + void setFromLua(bool newFromLua) { + m_fromLua = newFromLua; + } + private: std::string getScriptTypeName() const override; @@ -290,10 +294,13 @@ class MoveEvent final : public Script, public SharedObject { std::map vocEquipMap; bool tileItem = false; + bool m_fromLua = true; + std::vector itemIdVector; std::vector actionIdVector; std::vector uniqueIdVector; std::vector positionVector; friend class MoveEventFunctions; + friend class ItemParse; }; diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 8dcbb3fc26a..a3b8f5c2d6f 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1625,11 +1625,11 @@ std::string formatPrice(std::string price, bool space /* = false*/) { return price; } -std::vector split(const std::string &str) { +std::vector split(const std::string &str, char delimiter /* = ','*/) { std::vector tokens; std::string token; std::istringstream tokenStream(str); - while (std::getline(tokenStream, token, ',')) { + while (std::getline(tokenStream, token, delimiter)) { auto trimedToken = token; trimString(trimedToken); tokens.push_back(trimedToken); diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 0d077d6a327..39200e0797d 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -137,7 +137,7 @@ SpellGroup_t stringToSpellGroup(const std::string &value); uint8_t forgeBonus(int32_t number); std::string formatPrice(std::string price, bool space /* = false*/); -std::vector split(const std::string &str); +std::vector split(const std::string &str, char delimiter = ','); std::string getFormattedTimeRemaining(uint32_t time); static inline unsigned int getNumberOfCores() {