diff --git a/config.lua.dist b/config.lua.dist
index b731fadbcd7..ed2561d4532 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -52,7 +52,8 @@ cleanProtectionZones = false
-- Connection Config
-- NOTE: allowOldProtocol can allow login on 10x protocol. (11.00)
-- NOTE: maxPlayers set to 0 means no limit
--- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25
+-- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25,
+-- It's recommended to use a range like min 50 in this function, otherwise you will be disconnected after equipping two-handed distance weapons.
ip = "127.0.0.1"
allowOldProtocol = false
bindOnlyGlobalAddress = false
@@ -80,6 +81,17 @@ freeDepotLimit = 2000
premiumDepotLimit = 10000
depotBoxes = 20
+-- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602)
+-- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact".
+-- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above.
+-- NOTE: The values represent percentage.
+-- NOTE: augmentIncreasedDamagePercent = value between 1 and 100 (damage percent to increase. ex: 5 = 5%, 50 = 50%)
+-- NOTE: augmentPowerfulImpactPercent = value between 1 and 100 (damage percent to increase. ex: 10 = 10%, 100 = 100%)
+-- NOTE: augmentStrongImpactPercent = value between 1 and 100 (damage percent to increase. ex: 7 = 7%, 70 = 70%)
+augmentIncreasedDamagePercent = 5
+augmentPowerfulImpactPercent = 7
+augmentStrongImpactPercent = 10
+
-- Prey system
-- NOTE: preyRerollPricePerLevel: Price multiplier in gold coin for rerolling prey list.
-- NOTE: preySelectListPrice: Price to manually select creature on list and to lock prey slot.
diff --git a/data/items/items.xml b/data/items/items.xml
index 0858df99219..45cde62bd9d 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -75009,6 +75009,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75032,6 +75035,12 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
@@ -75055,6 +75064,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75078,6 +75090,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75101,6 +75116,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75124,6 +75142,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75149,6 +75170,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75173,6 +75197,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75197,6 +75224,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75221,6 +75251,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75245,6 +75278,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75269,6 +75305,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75285,6 +75324,14 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
@@ -75311,6 +75358,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75338,6 +75388,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75365,6 +75418,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75392,6 +75448,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75407,6 +75466,14 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
@@ -75430,6 +75497,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75459,6 +75530,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75483,6 +75558,17 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
+
+
+
@@ -75506,6 +75592,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75535,6 +75625,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75559,6 +75653,17 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 4abf29c04cb..30a4b172af1 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -17,6 +17,9 @@ enum ConfigKey_t : uint16_t {
ALLOW_BLOCK_SPAWN,
ALLOW_CHANGEOUTFIT,
ALLOW_RELOAD,
+ AUGMENT_INCREASED_DAMAGE_PERCENT,
+ AUGMENT_POWERFUL_IMPACT_PERCENT,
+ AUGMENT_STRONG_IMPACT_PERCENT,
AUTH_TYPE,
AUTOBANK,
AUTOLOOT,
@@ -324,5 +327,5 @@ enum ConfigKey_t : uint16_t {
WHEEL_POINTS_PER_LEVEL,
WHITE_SKULL_TIME,
WORLD_TYPE,
- XP_DISPLAY_MODE,
+ XP_DISPLAY_MODE
};
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 8d4ba7a77da..0525c5a560a 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -345,6 +345,9 @@ bool ConfigManager::load() {
loadIntConfig(L, WHEEL_ATELIER_ROTATE_REGULAR_COST, "wheelAtelierRotateRegularCost", 250000);
loadIntConfig(L, WHEEL_POINTS_PER_LEVEL, "wheelPointsPerLevel", 1);
loadIntConfig(L, WHITE_SKULL_TIME, "whiteSkullTime", 15 * 60 * 1000);
+ loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5);
+ loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10);
+ loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7);
loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data");
loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global");
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 56cca63fe56..06812095cbf 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -98,7 +98,11 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr creature, std::sh
}
}
}
+ if (attackerPlayer && wheelSpell && wheelSpell->isInstant()) {
+ wheelSpell->getCombatDataAugment(attackerPlayer, damage);
+ }
}
+
return damage;
}
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index 60cc5e7d2a9..3aa445b34aa 100644
--- a/src/creatures/combat/spells.cpp
+++ b/src/creatures/combat/spells.cpp
@@ -630,6 +630,43 @@ void Spell::setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t gr
}
}
+void Spell::getCombatDataAugment(std::shared_ptr player, CombatDamage &damage) {
+ if (!(damage.instantSpellName).empty()) {
+ const auto equippedAugmentItems = player->getEquippedAugmentItems();
+ for (const auto &item : equippedAugmentItems) {
+ const auto augments = item->getAugmentsBySpellName(damage.instantSpellName);
+ for (auto &augment : augments) {
+ if (augment->value == 0) {
+ continue;
+ }
+ if (augment->type == Augment_t::IncreasedDamage || augment->type == Augment_t::PowerfulImpact || augment->type == Augment_t::StrongImpact) {
+ const float augmentPercent = augment->value / 100.0;
+ damage.primary.value += static_cast(damage.primary.value * augmentPercent);
+ damage.secondary.value += static_cast(damage.secondary.value * augmentPercent);
+ } else if (augment->type != Augment_t::Cooldown) {
+ const int32_t augmentValue = augment->value * 100;
+ damage.lifeLeech += augment->type == Augment_t::LifeLeech ? augmentValue : 0;
+ damage.manaLeech += augment->type == Augment_t::ManaLeech ? augmentValue : 0;
+ damage.criticalDamage += augment->type == Augment_t::CriticalExtraDamage ? augmentValue : 0;
+ }
+ }
+ }
+ }
+};
+
+int32_t Spell::calculateAugmentSpellCooldownReduction(std::shared_ptr player) const {
+ int32_t spellCooldown = 0;
+ const auto equippedAugmentItems = player->getEquippedAugmentItemsByType(Augment_t::Cooldown);
+ for (const auto &item : equippedAugmentItems) {
+ const auto augments = item->getAugmentsBySpellNameAndType(getName(), Augment_t::Cooldown);
+ for (auto &augment : augments) {
+ spellCooldown += augment->value;
+ }
+ }
+
+ return spellCooldown;
+}
+
void Spell::applyCooldownConditions(std::shared_ptr player) const {
WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
bool isUpgraded = getWheelOfDestinyUpgraded() && static_cast(spellGrade) > 0;
@@ -644,8 +681,10 @@ void Spell::applyCooldownConditions(std::shared_ptr player) const {
if (isUpgraded) {
spellCooldown -= getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, spellGrade);
}
- g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN));
+ int32_t augmentCooldownReduction = calculateAugmentSpellCooldownReduction(player);
+ g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}, augment {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN), augmentCooldownReduction);
spellCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN);
+ spellCooldown -= augmentCooldownReduction;
if (spellCooldown > 0) {
std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, m_spellId);
player->addCondition(condition);
diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp
index cd1a7aaa623..dce7b3caba4 100644
--- a/src/creatures/combat/spells.hpp
+++ b/src/creatures/combat/spells.hpp
@@ -345,6 +345,9 @@ class Spell : public BaseSpell {
m_separator = newSeparator.data();
}
+ void getCombatDataAugment(std::shared_ptr player, CombatDamage &damage);
+ int32_t calculateAugmentSpellCooldownReduction(std::shared_ptr player) const;
+
protected:
void applyCooldownConditions(std::shared_ptr player) const;
bool playerSpellCheck(std::shared_ptr player) const;
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 925086f2b53..78478953ade 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -4115,6 +4115,35 @@ std::vector> Player::getAllInventoryItems(bool ignoreEquip
return itemVector;
}
+std::vector> Player::getEquippedAugmentItemsByType(Augment_t augmentType) const {
+ std::vector> equippedAugmentItemsByType;
+ const auto equippedAugmentItems = getEquippedItems();
+
+ for (const auto &item : equippedAugmentItems) {
+ for (auto &augment : item->getAugments()) {
+ if (augment->type == augmentType) {
+ equippedAugmentItemsByType.push_back(item);
+ }
+ }
+ }
+
+ return equippedAugmentItemsByType;
+}
+
+std::vector> Player::getEquippedAugmentItems() const {
+ std::vector> equippedAugmentItems;
+ const auto equippedItems = getEquippedItems();
+
+ for (const auto &item : equippedItems) {
+ if (item->getAugments().size() < 1) {
+ continue;
+ }
+ equippedAugmentItems.push_back(item);
+ }
+
+ return equippedAugmentItems;
+}
+
std::vector> Player::getEquippedItems() const {
std::vector valid_slots {
CONST_SLOT_HEAD,
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index da4c0cec9e8..b3bdd875b20 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -2601,6 +2601,12 @@ class Player final : public Creature, public Cylinder, public Bankable {
// This get all blessings
phmap::flat_hash_map getBlessingNames() const;
+ // Gets the equipped items with augment by type
+ std::vector> getEquippedAugmentItemsByType(Augment_t augmentType) const;
+
+ // Gets the equipped items with augment
+ std::vector> getEquippedAugmentItems() const;
+
/**
* @brief Get the equipped items of the player->
* @details This function returns a vector containing the items currently equipped by the player
diff --git a/src/enums/item_attribute.hpp b/src/enums/item_attribute.hpp
index adc49c11a19..be7b7445710 100644
--- a/src/enums/item_attribute.hpp
+++ b/src/enums/item_attribute.hpp
@@ -46,6 +46,7 @@ enum ItemAttribute_t : uint64_t {
LOOTMESSAGE_SUFFIX = 33,
STORE_INBOX_CATEGORY = 34,
OBTAINCONTAINER = 35,
+ AUGMENTS = 36,
};
enum ItemDecayState_t : uint8_t {
diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp
index 2d1ba9301c5..1f247b611bc 100644
--- a/src/items/functions/item/item_parse.cpp
+++ b/src/items/functions/item/item_parse.cpp
@@ -68,6 +68,7 @@ void ItemParse::initParse(const std::string &tmpStrValue, pugi::xml_node attribu
ItemParse::parseWalk(tmpStrValue, valueAttribute, itemType);
ItemParse::parseAllowDistanceRead(tmpStrValue, valueAttribute, itemType);
ItemParse::parseImbuement(tmpStrValue, attributeNode, valueAttribute, itemType);
+ ItemParse::parseAugment(tmpStrValue, attributeNode, valueAttribute, itemType);
ItemParse::parseStackSize(tmpStrValue, valueAttribute, itemType);
ItemParse::parseSpecializedMagicLevelPoint(tmpStrValue, valueAttribute, itemType);
ItemParse::parseMagicShieldCapacity(tmpStrValue, valueAttribute, itemType);
@@ -870,6 +871,58 @@ void ItemParse::parseImbuement(const std::string &tmpStrValue, pugi::xml_node at
}
}
+void ItemParse::parseAugment(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) {
+ if (tmpStrValue != "augments") {
+ return;
+ }
+
+ // Check if the augments value is 1 or 0 (1 = enable - 0 = disable)
+ if (valueAttribute.as_bool()) {
+ for (const auto subAttributeNode : attributeNode.children()) {
+ const pugi::xml_attribute subKeyAttribute = subAttributeNode.attribute("key");
+ if (!subKeyAttribute) {
+ continue;
+ }
+
+ const pugi::xml_attribute subValueAttribute = subAttributeNode.attribute("value");
+ if (!subValueAttribute) {
+ continue;
+ }
+
+ const auto &augmentEnum = magic_enum::enum_cast(toPascalCase(subValueAttribute.as_string()));
+ if (augmentEnum.has_value()) {
+ const Augment_t augmentType = augmentEnum.value();
+ g_logger().trace("[ParseAugment::initParseAugment] - Item '{}' has an augment '{}'", itemType.name, subValueAttribute.as_string());
+ int32_t augmentValue = 0;
+ const bool hasValueDescrition = isAugmentWithoutValueDescription(augmentType);
+
+ if (hasValueDescrition) {
+ const auto it = AugmentWithoutValueDescriptionDefaultKeys.find(augmentType);
+ if (it != AugmentWithoutValueDescriptionDefaultKeys.end()) {
+ augmentValue = g_configManager().getNumber(it->second, __FUNCTION__);
+ }
+ }
+
+ const auto augmentName = asLowerCaseString(subKeyAttribute.as_string());
+ const pugi::xml_object_range augmentValueAttributeNode = subAttributeNode.children();
+ if (!augmentValueAttributeNode.empty()) {
+ const pugi::xml_node augmentValueNode = *augmentValueAttributeNode.begin();
+ const pugi::xml_attribute augmentValueAttribute = augmentValueNode.attribute("value");
+ augmentValue = augmentValueAttribute ? pugi::cast(augmentValueAttribute.value()) : augmentValue;
+ } else if (!hasValueDescrition) {
+ g_logger().warn("[{}] - Item '{}' has an augment '{}' without a value", __FUNCTION__, itemType.name, augmentName);
+ }
+
+ if (augmentType != Augment_t::None) {
+ itemType.addAugment(augmentName, augmentType, augmentValue);
+ }
+ } else {
+ g_logger().warn("[{}] - Unknown type '{}'", __FUNCTION__, subValueAttribute.as_string());
+ }
+ }
+ }
+}
+
void ItemParse::parseStackSize(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) {
std::string stringValue = tmpStrValue;
if (stringValue == "stacksize") {
diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp
index 7859a77fe9c..f63b6bc0689 100644
--- a/src/items/functions/item/item_parse.hpp
+++ b/src/items/functions/item/item_parse.hpp
@@ -158,6 +158,7 @@ const phmap::flat_hash_map ItemParseAttribut
{ "primarytype", ITEM_PARSE_PRIMARYTYPE },
{ "usedbyhouseguests", ITEM_PARSE_USEDBYGUESTS },
{ "script", ITEM_PARSE_SCRIPT },
+ { "augments", ITEM_PARSE_AUGMENT }
};
const phmap::flat_hash_map ItemTypesMap = {
@@ -247,6 +248,12 @@ const phmap::flat_hash_map ImbuementsTypeMap = {
{ "increase capacity", IMBUEMENT_INCREASE_CAPACITY }
};
+const phmap::flat_hash_map AugmentWithoutValueDescriptionDefaultKeys = {
+ { Augment_t::IncreasedDamage, AUGMENT_INCREASED_DAMAGE_PERCENT },
+ { Augment_t::PowerfulImpact, AUGMENT_POWERFUL_IMPACT_PERCENT },
+ { Augment_t::StrongImpact, AUGMENT_STRONG_IMPACT_PERCENT },
+};
+
class ItemParse : public Items {
public:
static void initParse(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType);
@@ -304,6 +311,7 @@ class ItemParse : public Items {
static void parseWalk(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType);
static void parseAllowDistanceRead(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType);
static void parseImbuement(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType);
+ static void parseAugment(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType);
static void parseStackSize(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType);
static void parseSpecializedMagicLevelPoint(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType);
static void parseMagicShieldCapacity(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType);
diff --git a/src/items/item.cpp b/src/items/item.cpp
index 81d24a790e7..104efc7f925 100644
--- a/src/items/item.cpp
+++ b/src/items/item.cpp
@@ -1437,6 +1437,11 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr- item /*= nullptr
}
}
+ std::string augmentsDescription = parseAugmentDescription(item, true);
+ if (!augmentsDescription.empty()) {
+ descriptions.emplace_back("Augments", augmentsDescription);
+ }
+
if (it.isKey()) {
ss.str("");
ss << fmt::format("{:04}", item->getAttribute(ItemAttribute_t::ACTIONID));
@@ -1789,6 +1794,11 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr
- item /*= nullptr
descriptions.emplace_back("Imbuement Slots", std::to_string(it.imbuementSlot));
}
+ std::string augmentsDescription = it.parseAugmentDescription(true);
+ if (!augmentsDescription.empty()) {
+ descriptions.emplace_back("Augments", augmentsDescription);
+ }
+
if (it.isKey()) {
ss.str("");
ss << fmt::format("{:04}", 0);
@@ -3017,6 +3027,8 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std::
s << '.';
}
+ s << parseAugmentDescription(item);
+
s << parseImbuementDescription(item);
s << parseClassificationDescription(item);
diff --git a/src/items/item.hpp b/src/items/item.hpp
index 9ff0d3d15aa..a9774c902cd 100644
--- a/src/items/item.hpp
+++ b/src/items/item.hpp
@@ -291,6 +291,12 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject {
return isStoreItem() || hasOwner();
}
+ static std::string parseAugmentDescription(std::shared_ptr
- item, bool inspect = false) {
+ if (!item) {
+ return "";
+ }
+ return items[item->getID()].parseAugmentDescription(inspect);
+ }
static std::string parseImbuementDescription(std::shared_ptr
- item);
static std::string parseShowDurationSpeed(int32_t speed, bool &begin);
static std::string parseShowDuration(std::shared_ptr
- item);
@@ -419,6 +425,29 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject {
}
return items[id].extraDefense;
}
+ std::vector> getAugments() const {
+ return items[id].augments;
+ }
+ std::vector> getAugmentsBySpellNameAndType(std::string spellName, Augment_t augmentType) const {
+ std::vector> augments;
+ for (auto &augment : items[id].augments) {
+ if (strcasecmp(augment->spellName.c_str(), spellName.c_str()) == 0 && augment->type == augmentType) {
+ augments.push_back(augment);
+ }
+ }
+
+ return augments;
+ }
+ std::vector> getAugmentsBySpellName(std::string spellName) const {
+ std::vector> augments;
+ for (auto &augment : items[id].augments) {
+ if (strcasecmp(augment->spellName.c_str(), spellName.c_str()) == 0) {
+ augments.push_back(augment);
+ }
+ }
+
+ return augments;
+ }
uint8_t getImbuementSlot() const {
if (hasAttribute(ItemAttribute_t::IMBUEMENT_SLOT)) {
return getAttribute(ItemAttribute_t::IMBUEMENT_SLOT);
diff --git a/src/items/items.cpp b/src/items/items.cpp
index 9e31f5f105f..7d8ff0cf7c0 100644
--- a/src/items/items.cpp
+++ b/src/items/items.cpp
@@ -67,6 +67,47 @@ ItemTypes_t Items::getLootType(const std::string &strValue) {
return ITEM_TYPE_NONE;
}
+const std::string Items::getAugmentNameByType(Augment_t augmentType) {
+ std::string augmentTypeName = magic_enum::enum_name(augmentType).data();
+ augmentTypeName = toStartCaseWithSpace(augmentTypeName);
+ if (!isAugmentWithoutValueDescription(augmentType)) {
+ toLowerCaseString(augmentTypeName);
+ }
+ return augmentTypeName;
+}
+
+std::string ItemType::parseAugmentDescription(bool inspect /*= false*/) const {
+ if (augments.empty()) {
+ return "";
+ }
+
+ std::vector descriptions;
+ for (const auto &augment : augments) {
+ descriptions.push_back(getFormattedAugmentDescription(augment));
+ }
+
+ if (inspect) {
+ return fmt::format("{}.", fmt::join(descriptions.begin(), descriptions.end(), ", "));
+ } else {
+ return fmt::format("\nAugments: ({}).", fmt::join(descriptions.begin(), descriptions.end(), ", "));
+ }
+}
+
+std::string ItemType::getFormattedAugmentDescription(const std::shared_ptr &augmentInfo) const {
+ const std::string augmentName = Items::getAugmentNameByType(augmentInfo->type);
+ std::string augmentSpellNameCapitalized = augmentInfo->spellName;
+ capitalizeWordsIgnoringString(augmentSpellNameCapitalized, " of ");
+
+ char signal = augmentInfo->value > 0 ? '-' : '+';
+
+ if (Items::isAugmentWithoutValueDescription(augmentInfo->type)) {
+ return fmt::format("{} -> {}", augmentSpellNameCapitalized, augmentName);
+ } else if (augmentInfo->type == Augment_t::Cooldown) {
+ return fmt::format("{} -> {}{}s {}", augmentSpellNameCapitalized, signal, augmentInfo->value / 1000, augmentName);
+ }
+ return fmt::format("{} -> {:+}% {}", augmentSpellNameCapitalized, augmentInfo->value, augmentName);
+}
+
bool Items::reload() {
clear();
loadFromProtobuf();
diff --git a/src/items/items.hpp b/src/items/items.hpp
index f9e48ae1032..82af0659af4 100644
--- a/src/items/items.hpp
+++ b/src/items/items.hpp
@@ -252,6 +252,14 @@ class ItemType {
return str;
}
+ std::string parseAugmentDescription(bool inspect = false) const;
+ std::string getFormattedAugmentDescription(const std::shared_ptr &augmentInfo) const;
+
+ void addAugment(std::string spellName, Augment_t augmentType, int32_t value) {
+ auto augmentInfo = std::make_shared(spellName, augmentType, value);
+ augments.emplace_back(augmentInfo);
+ }
+
void setImbuementType(ImbuementTypes_t imbuementType, uint16_t slotMaxTier) {
imbuementTypes[imbuementType] = std::min(IMBUEMENT_MAX_TIER, slotMaxTier);
}
@@ -331,6 +339,8 @@ class ItemType {
int8_t hitChance = 0;
+ std::vector> augments;
+
// 12.90
bool wearOut = false;
bool clockExpire = false;
@@ -435,6 +445,18 @@ class Items {
return dummys;
}
+ static const std::string getAugmentNameByType(Augment_t augmentType);
+
+ static const bool isAugmentWithoutValueDescription(Augment_t augmentType) {
+ static std::vector vector = {
+ Augment_t::IncreasedDamage,
+ Augment_t::PowerfulImpact,
+ Augment_t::StrongImpact,
+ };
+
+ return std::find(vector.begin(), vector.end(), augmentType) != vector.end();
+ }
+
private:
std::vector items;
std::vector ladders;
diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp
index f044d4b5248..badac532951 100644
--- a/src/items/items_definitions.hpp
+++ b/src/items/items_definitions.hpp
@@ -270,6 +270,17 @@ enum ImbuementTypes_t : int64_t {
IMBUEMENT_INCREASE_CAPACITY = 17
};
+enum class Augment_t : uint8_t {
+ None,
+ PowerfulImpact,
+ StrongImpact,
+ IncreasedDamage,
+ Cooldown,
+ CriticalExtraDamage,
+ LifeLeech,
+ ManaLeech
+};
+
enum class ContainerCategory_t : uint8_t {
All,
Ammunition,
@@ -606,9 +617,19 @@ enum ItemParseAttributes_t {
ITEM_PARSE_PRIMARYTYPE,
ITEM_PARSE_USEDBYGUESTS,
ITEM_PARSE_SCRIPT,
+ ITEM_PARSE_AUGMENT,
};
struct ImbuementInfo {
Imbuement* imbuement;
uint32_t duration = 0;
};
+
+struct AugmentInfo {
+ AugmentInfo(std::string spellName, Augment_t type, int32_t value) :
+ spellName(std::move(spellName)), type(type), value(value) { }
+
+ std::string spellName;
+ Augment_t type;
+ int32_t value;
+};
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index d924e0bfb0e..ab111fd3c8f 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -5717,7 +5717,12 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) {
}
if (!oldProtocol) {
- msg.add(0x00); // Augment
+ std::string augmentsDescription = it.parseAugmentDescription(true);
+ if (!augmentsDescription.empty()) {
+ msg.addString(augmentsDescription, "ProtocolGame::sendMarketDetail - augmentsDescription");
+ } else {
+ msg.add(0x00); // no augments
+ }
}
if (it.imbuementSlot > 0) {
diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp
index 0d8b39a485d..b1fe671f416 100644
--- a/src/utils/tools.cpp
+++ b/src/utils/tools.cpp
@@ -1558,6 +1558,22 @@ void capitalizeWords(std::string &source) {
}
}
+void capitalizeWordsIgnoringString(std::string &source, const std::string stringToIgnore) {
+ toLowerCaseString(source);
+ uint8_t size = (uint8_t)source.size();
+ uint8_t indexFound = source.find(stringToIgnore);
+ for (uint8_t i = 0; i < size; i++) {
+ if (indexFound != std::string::npos && (i > indexFound - 1) && i < (indexFound + stringToIgnore.size())) {
+ continue;
+ }
+ if (i == 0) {
+ source[i] = (char)toupper(source[i]);
+ } else if (source[i - 1] == ' ' || source[i - 1] == '\'') {
+ source[i] = (char)toupper(source[i]);
+ }
+ }
+}
+
/**
* @details
* Prevents the console from closing so there is time to read the error information
diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp
index 769fb5d68d3..25683bf22bb 100644
--- a/src/utils/tools.hpp
+++ b/src/utils/tools.hpp
@@ -138,6 +138,7 @@ const char* getReturnMessage(ReturnValue value);
void sleep_for(uint64_t ms);
void capitalizeWords(std::string &source);
+void capitalizeWordsIgnoringString(std::string &source, const std::string stringToIgnore);
void consoleHandlerExit();
NameEval_t validateName(const std::string &name);