diff --git a/data/XML/imbuements.xml b/data/XML/imbuements.xml index 0a2fd90702f..c00a2b8d3e1 100644 --- a/data/XML/imbuements.xml +++ b/data/XML/imbuements.xml @@ -22,6 +22,7 @@ + @@ -427,4 +428,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/data/items/items.xml b/data/items/items.xml index 0858df99219..e4e4208cb82 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -8566,7 +8566,7 @@ - + diff --git a/data/scripts/runes/paralyze_rune.lua b/data/scripts/runes/paralyze_rune.lua index b0c602a159b..e055ab12421 100644 --- a/data/scripts/runes/paralyze_rune.lua +++ b/data/scripts/runes/paralyze_rune.lua @@ -3,7 +3,7 @@ combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_RED) combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_UNDEFINEDDAMAGE) local condition = Condition(CONDITION_PARALYZE) -condition:setParameter(CONDITION_PARAM_TICKS, 6000) +condition:setParameter(CONDITION_PARAM_TICKS, 60000) condition:setFormula(-1, 0, -1, 0) combat:addCondition(condition) @@ -29,8 +29,8 @@ rune:charges(1) rune:setPzLocked(true) rune:level(54) rune:magicLevel(18) -rune:cooldown(6 * 1000) -rune:groupCooldown(2 * 1000) +rune:cooldown(1 * 1000) +rune:groupCooldown(1 * 1000) rune:mana(1400) rune:needTarget(true) rune:isBlocking(true) -- True = Solid / False = Creature diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 56cca63fe56..823a029ea33 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -753,23 +753,64 @@ void Combat::CombatConditionFunc(std::shared_ptr caster, std::shared_p if (condition->getType() == CONDITION_FEARED && !checkFearConditionAffected(player)) { return; } - } - if (caster == target || target && !target->isImmune(condition->getType())) { - auto conditionCopy = condition->clone(); - if (caster) { - conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); - conditionCopy->setPositionParam(CONDITION_PARAM_CASTER_POSITION, caster->getPosition()); + if (condition->getType() == CONDITION_PARALYZE && target) { + if (player) { + if (reflectParalyzeCondition(caster, player, condition)) { + return; + } + } } - // TODO: infight condition until all aggressive conditions has ended - if (target) { - target->addCombatCondition(conditionCopy, caster && caster->getPlayer() != nullptr); + if (caster == target || (target && !target->isImmune(condition->getType()))) { + auto conditionCopy = condition->clone(); + if (caster) { + conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); + conditionCopy->setPositionParam(CONDITION_PARAM_CASTER_POSITION, caster->getPosition()); + } + + // TODO: infight condition until all aggressive conditions has ended + if (target) { + target->addCombatCondition(conditionCopy, caster && caster->getPlayer() != nullptr); + } } } } } +bool Combat::reflectParalyzeCondition(const std::shared_ptr &caster, const std::shared_ptr &target, const std::shared_ptr &condition) { + if (!target->hasCondition(CONDITION_PARALYZE)) { + target->addCondition(condition->clone()); + return true; + } + + int32_t reflectionChance = 0; + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + auto item = target->getInventoryItem(static_cast(slot)); + if (item) { + for (uint8_t slotid = 0; slotid < item->getImbuementSlot(); ++slotid) { + ImbuementInfo imbuementInfo; + if (item->getImbuementInfo(slotid, &imbuementInfo)) { + reflectionChance = std::max(reflectionChance, imbuementInfo.imbuement->paralyzeReduction); + } + } + } + } + + if (caster && !caster->isImmune(CONDITION_PARALYZE)) { + caster->addCondition(condition->clone()); + } + + if (uniform_random(1, 100) <= reflectionChance) { + if (target->hasCondition(CONDITION_PARALYZE)) { + target->removeCondition(CONDITION_PARALYZE); + } + return true; + } + + return false; +} + void Combat::CombatDispelFunc(std::shared_ptr, std::shared_ptr target, const CombatParams ¶ms, CombatDamage*) { if (target) { target->removeCombatCondition(params.dispelType); diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 6a79455c7c4..206f90de94b 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -385,6 +385,8 @@ class Combat { static void combatTileEffects(const CreatureVector &spectators, std::shared_ptr caster, std::shared_ptr tile, const CombatParams ¶ms); + static bool reflectParalyzeCondition(const std::shared_ptr &caster, const std::shared_ptr &target, const std::shared_ptr &condition); + /** * @brief Calculate the level formula for combat. * diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp index ed312dbf8e1..6e114c69cd6 100644 --- a/src/creatures/players/imbuements/imbuements.cpp +++ b/src/creatures/players/imbuements/imbuements.cpp @@ -12,7 +12,30 @@ #include "lua/creature/events.hpp" #include "utils/pugicast.hpp" -Imbuement* Imbuements::getImbuement(uint16_t id) { +static const std::unordered_map skillMap = { + { "sword", SKILL_SWORD }, + { "axe", SKILL_AXE }, + { "club", SKILL_CLUB }, + { "dist", SKILL_DISTANCE }, + { "distance", SKILL_DISTANCE }, + { "fish", SKILL_FISHING }, + { "shield", SKILL_SHIELD }, + { "fist", SKILL_FIST }, + { "magicpoints", STAT_MAGICPOINTS }, + { "critical", SKILL_CRITICAL_HIT_DAMAGE }, + { "lifeleech", SKILL_LIFE_LEECH_AMOUNT }, + { "manaleech", SKILL_MANA_LEECH_AMOUNT } +}; + +static const std::unordered_map effectTypeToSkillMode = { + { "skill", NormalSkill }, + { "magicpoints", MagicLevel }, + { "critical", SpecialSkill }, + { "lifeleech", SpecialSkill }, + { "manaleech", SpecialSkill } +}; + +std::shared_ptr Imbuements::getImbuement(uint16_t id) const { if (id == 0) { return nullptr; } @@ -22,7 +45,7 @@ Imbuement* Imbuements::getImbuement(uint16_t id) { g_logger().warn("Imbuement {} not found", id); return nullptr; } - return &it->second; + return it->second; } bool Imbuements::loadFromXml(bool /* reloading */) { @@ -35,277 +58,289 @@ bool Imbuements::loadFromXml(bool /* reloading */) { } loaded = true; - for (auto baseNode : doc.child("imbuements").children()) { - pugi::xml_attribute attr; - // Base for imbue - if (strcasecmp(baseNode.name(), "base") == 0) { - pugi::xml_attribute id = baseNode.attribute("id"); - if (!id) { - g_logger().warn("Missing id for base entry"); - continue; - } - basesImbuement.emplace_back( - pugi::cast(id.value()), - baseNode.attribute("name").as_string(), - pugi::cast(baseNode.attribute("price").value()), - pugi::cast(baseNode.attribute("protectionPrice").value()), - pugi::cast(baseNode.attribute("removecost").value()), - pugi::cast(baseNode.attribute("duration").value()), - pugi::cast(baseNode.attribute("percent").value()) - ); - - // Category/Group - } else if (strcasecmp(baseNode.name(), "category") == 0) { - pugi::xml_attribute id = baseNode.attribute("id"); - if (!id) { - g_logger().warn("Missing id for category entry"); - continue; - } - categoriesImbuement.emplace_back( - pugi::cast(id.value()), - baseNode.attribute("name").as_string(), - baseNode.attribute("agressive").as_bool(true) - ); - - // Imbuements - } else if (strcasecmp(baseNode.name(), "imbuement") == 0) { + if (!std::ranges::all_of(doc.child("imbuements").children(), [&](const auto &baseNode) { ++runningid; - pugi::xml_attribute base = baseNode.attribute("base"); - if (!base) { - g_logger().warn("Missing imbuement base id"); - continue; + if (strcasecmp(baseNode.name(), "base") == 0) { + if (!processBaseNode(baseNode)) { + g_logger().error("Falha ao processar o nó base"); + return false; + } + } else if (strcasecmp(baseNode.name(), "category") == 0) { + if (!processCategoryNode(baseNode)) { + g_logger().error("Falha ao processar o nó category"); + return false; + } + } else if (strcasecmp(baseNode.name(), "imbuement") == 0) { + if (!processImbuementNode(baseNode)) { + g_logger().error("Falha ao processar o nó imbuement"); + return false; + } } + return true; + })) { + return false; + } + return true; +} - uint16_t baseid = pugi::cast(base.value()); - auto groupBase = getBaseByID(baseid); - if (groupBase == nullptr) { - g_logger().warn("Group base '{}' not exist", baseid); - continue; - } +bool Imbuements::processBaseNode(const pugi::xml_node &baseNode) { + pugi::xml_attribute id = baseNode.attribute("id"); + if (!id) { + g_logger().warn("Missing id for base entry"); + return false; + } - auto imbuements = imbuementMap.emplace(std::piecewise_construct, std::forward_as_tuple(runningid), std::forward_as_tuple(runningid, baseid)); + basesImbuement.emplace_back(std::make_shared( + pugi::cast(id.value()), + baseNode.attribute("name").as_string(), + pugi::cast(baseNode.attribute("price").value()), + pugi::cast(baseNode.attribute("protectionPrice").value()), + pugi::cast(baseNode.attribute("removecost").value()), + pugi::cast(baseNode.attribute("duration").value()), + pugi::cast(baseNode.attribute("percent").value()) + )); - if (!imbuements.second) { - g_logger().warn("Duplicate imbuement of Base ID: '{}' ignored", baseid); - continue; - } + return true; +} - Imbuement &imbuement = imbuements.first->second; +bool Imbuements::processCategoryNode(const pugi::xml_node &categoryNode) { + pugi::xml_attribute id = categoryNode.attribute("id"); + if (!id) { + g_logger().warn("Missing id for category entry"); + return false; + } - pugi::xml_attribute iconBase = baseNode.attribute("iconid"); - if (!iconBase) { - g_logger().warn("Missing 'iconid' for imbuement entry"); - continue; - } - imbuement.icon = pugi::cast(iconBase.value()); + categoriesImbuement.emplace_back(std::make_shared( + pugi::cast(id.value()), + categoryNode.attribute("name").as_string(), + categoryNode.attribute("agressive").as_bool(true) + )); - if (pugi::xml_attribute soundBase = baseNode.attribute("sound")) { - imbuement.soundEffect = static_cast(pugi::cast(soundBase.value())); - } + return true; +} + +bool Imbuements::processImbuementNode(const pugi::xml_node &imbuementNode) { + pugi::xml_attribute base = imbuementNode.attribute("base"); + if (!base) { + g_logger().warn("Missing imbuement base id"); + return false; + } + + uint16_t baseid = pugi::cast(base.value()); + const auto &groupBase = getBaseByID(baseid); + if (groupBase == nullptr) { + g_logger().warn("Group base '{}' not exist", baseid); + return false; + } + + auto imbuements = imbuementMap.emplace(std::piecewise_construct, std::forward_as_tuple(runningid), std::forward_as_tuple(std::make_shared(runningid, baseid))); + + if (!imbuements.second) { + g_logger().warn("Duplicate imbuement of Base ID: '{}' ignored", baseid); + return false; + } + + const auto &imbuement = imbuements.first->second; + + pugi::xml_attribute iconBase = imbuementNode.attribute("iconid"); + if (!iconBase) { + g_logger().warn("Missing 'iconid' for imbuement entry"); + return false; + } + imbuement->icon = pugi::cast(iconBase.value()); + + if (pugi::xml_attribute soundBase = imbuementNode.attribute("sound")) { + imbuement->soundEffect = static_cast(pugi::cast(soundBase.value())); + } + + pugi::xml_attribute premiumBase = imbuementNode.attribute("premium"); + if (premiumBase) { + imbuement->premium = premiumBase.as_bool(); + } + + if (pugi::xml_attribute storageBase = imbuementNode.attribute("storage")) { + imbuement->storage = pugi::cast(storageBase.value()); + } - pugi::xml_attribute premiumBase = baseNode.attribute("premium"); - if (premiumBase) { - imbuement.premium = premiumBase.as_bool(); + pugi::xml_attribute subgroupBase = imbuementNode.attribute("subgroup"); + if (subgroupBase) { + imbuement->subgroup = subgroupBase.as_string(); + } + + pugi::xml_attribute categorybase = imbuementNode.attribute("category"); + if (!categorybase) { + g_logger().warn("Missing imbuement category"); + return false; + } + + auto category = pugi::cast(categorybase.value()); + const auto &category_p = getCategoryByID(category); + if (category_p == nullptr) { + g_logger().warn("Category imbuement {} not exist", category); + return false; + } + + imbuement->category = category; + + pugi::xml_attribute nameBase = imbuementNode.attribute("name"); + if (!nameBase) { + g_logger().warn("Missing imbuement name"); + return false; + } + imbuement->name = nameBase.value(); + + return processImbuementChildNodes(imbuementNode, imbuement); +} + +bool Imbuements::processImbuementChildNodes(const pugi::xml_node &imbuementNode, const std::shared_ptr &imbuement) { + pugi::xml_attribute attr; + for (auto childNode : imbuementNode.children()) { + if (!(attr = childNode.attribute("key"))) { + g_logger().warn("Missing key attribute in imbuement id: {}", runningid); + return false; + } + + std::string type = attr.as_string(); + if (strcasecmp(type.c_str(), "item") == 0) { + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing item ID for imbuement name '{}'", imbuement->name); + return false; } + auto sourceId = pugi::cast(attr.value()); - if (pugi::xml_attribute storageBase = baseNode.attribute("storage")) { - imbuement.storage = pugi::cast(storageBase.value()); + uint16_t count = 1; + if ((attr = childNode.attribute("count"))) { + count = pugi::cast(attr.value()); } - pugi::xml_attribute subgroupBase = baseNode.attribute("subgroup"); - if (subgroupBase) { - imbuement.subgroup = subgroupBase.as_string(); + auto it2 = std::find_if(imbuement->items.begin(), imbuement->items.end(), [sourceId](const std::pair &source) -> bool { + return source.first == sourceId; + }); + + if (it2 != imbuement->items.end()) { + g_logger().warn("Duplicate item: {}, imbument name: {} ignored", childNode.attribute("value").value(), imbuement->name); + return false; } - pugi::xml_attribute categorybase = baseNode.attribute("category"); - if (!categorybase) { - g_logger().warn("Missing imbuement category"); - continue; + imbuement->items.emplace_back(sourceId, count); + + } else if (strcasecmp(type.c_str(), "description") == 0) { + std::string description = imbuement->name; + if ((attr = childNode.attribute("value"))) { + description = attr.as_string(); } - uint16_t category = pugi::cast(categorybase.value()); - auto category_p = getCategoryByID(category); - if (category_p == nullptr) { - g_logger().warn("Category imbuement {} not exist", category); - continue; + imbuement->description = description; + } else if (strcasecmp(type.c_str(), "effect") == 0) { + // Effects + if (!(attr = childNode.attribute("type"))) { + g_logger().warn("Missing effect type for imbuement name: {}", imbuement->name); + return false; } - imbuement.category = category; + std::string effecttype = attr.as_string(); - pugi::xml_attribute nameBase = baseNode.attribute("name"); - if (!nameBase) { - g_logger().warn("Missing imbuement name"); - continue; - } - imbuement.name = nameBase.value(); + if (strcasecmp(effecttype.c_str(), "skill") == 0) { + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing effect value for imbuement name {}", imbuement->name); + return false; + } - for (auto childNode : baseNode.children()) { - if (!(attr = childNode.attribute("key"))) { - g_logger().warn("Missing key attribute in imbuement id: {}", runningid); - continue; + std::string skillName = asLowerCaseString(attr.as_string()); + auto it = skillMap.find(skillName); + if (it == skillMap.end()) { + g_logger().warn("Unknown skill name {} in imbuement name {}", skillName, imbuement->name); + return false; } - std::string type = attr.as_string(); - if (strcasecmp(type.c_str(), "item") == 0) { - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing item ID for imbuement name '{}'", imbuement.name); - continue; - } - uint16_t sourceId = pugi::cast(attr.value()); + uint8_t skillId = it->second; - uint16_t count = 1; - if ((attr = childNode.attribute("count"))) { - count = pugi::cast(childNode.attribute("count").value()); + if (!(attr = childNode.attribute("bonus"))) { + g_logger().warn("Missing skill bonus for imbuement name {}", imbuement->name); + return false; + } + auto bonus = pugi::cast(attr.value()); + + auto itSkillMode = effectTypeToSkillMode.find(skillName); + UseSkillMode useSkillMode = (itSkillMode != effectTypeToSkillMode.end()) ? itSkillMode->second : NormalSkill; + + if (useSkillMode == NormalSkill) { + imbuement->skills[skillId] = bonus; + } else if (useSkillMode == MagicLevel) { + imbuement->stats[skillId] = bonus; + } else if (useSkillMode == SpecialSkill) { + imbuement->skills[skillId] = bonus; + int32_t chance = 100; + if ((attr = childNode.attribute("chance"))) { + chance = std::min(10000, pugi::cast(attr.value())); } + imbuement->skills[skillId - 1] = chance; + } + } else if (strcasecmp(effecttype.c_str(), "damage") == 0) { + if (!(attr = childNode.attribute("combat"))) { + g_logger().warn("Missing combat for imbuement name {}", imbuement->name); + return false; + } - auto it2 = std::find_if(imbuement.items.begin(), imbuement.items.end(), [sourceId](const std::pair &source) -> bool { - return source.first == sourceId; - }); + CombatType_t combatType = getCombatTypeByName(attr.as_string()); + if (combatType == COMBAT_NONE) { + g_logger().warn("Unknown combat type for element {}", attr.as_string()); + return false; + } - if (it2 != imbuement.items.end()) { - g_logger().warn("Duplicate item: {}, imbument name: {} ignored", childNode.attribute("value").value(), imbuement.name); - continue; - } + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement->name); + return false; + } - imbuement.items.emplace_back(sourceId, count); + uint32_t percent = std::min(100, pugi::cast(attr.value())); - } else if (strcasecmp(type.c_str(), "description") == 0) { - std::string description = imbuement.name; - if ((attr = childNode.attribute("value"))) { - description = attr.as_string(); - } + imbuement->combatType = combatType; + imbuement->elementDamage = std::min(100, static_cast(percent)); + } else if (strcasecmp(effecttype.c_str(), "reduction") == 0) { + if (!(attr = childNode.attribute("combat"))) { + g_logger().warn("Missing combat for imbuement name {}", imbuement->name); + return false; + } - imbuement.description = description; - } else if (strcasecmp(type.c_str(), "effect") == 0) { - // Effects - if (!(attr = childNode.attribute("type"))) { - g_logger().warn("Missing effect type for imbuement name: {}", imbuement.name); - continue; - } + CombatType_t combatType = getCombatTypeByName(attr.as_string()); + if (combatType == COMBAT_NONE) { + g_logger().warn("Unknown combat type for element {}", attr.as_string()); + return false; + } - std::string effecttype = attr.as_string(); - - if (strcasecmp(effecttype.c_str(), "skill") == 0) { - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing effect value for imbuement name {}", imbuement.name); - continue; - } - - uint8_t usenormalskill = 1; // 1 = skill normal, 2 = magiclevel, 3 = leechs/crit - - uint8_t skillId; - std::string tmpStrValue = asLowerCaseString(attr.as_string()); - if (tmpStrValue == "sword") { - skillId = SKILL_SWORD; - } else if (tmpStrValue == "axe") { - skillId = SKILL_AXE; - } else if (tmpStrValue == "club") { - skillId = SKILL_CLUB; - } else if ((tmpStrValue == "dist") || (tmpStrValue == "distance")) { - skillId = SKILL_DISTANCE; - } else if (tmpStrValue == "fish") { - skillId = SKILL_FISHING; - } else if (tmpStrValue == "shield") { - skillId = SKILL_SHIELD; - } else if (tmpStrValue == "fist") { - skillId = SKILL_FIST; - } else if (tmpStrValue == "magicpoints") { - skillId = STAT_MAGICPOINTS; - usenormalskill = 2; - } else if (tmpStrValue == "critical") { - usenormalskill = 3; - skillId = SKILL_CRITICAL_HIT_DAMAGE; - } else if (tmpStrValue == "lifeleech") { - usenormalskill = 3; - skillId = SKILL_LIFE_LEECH_AMOUNT; - } else if (tmpStrValue == "manaleech") { - usenormalskill = 3; - skillId = SKILL_MANA_LEECH_AMOUNT; - } else { - g_logger().warn("Unknow skill name {} in imbuement name {}", tmpStrValue, imbuement.name); - continue; - } - - if (!(attr = childNode.attribute("bonus"))) { - g_logger().warn("Missing skill bonus for imbuement name {}", imbuement.name); - continue; - } - int32_t bonus = pugi::cast(attr.value()); - - if (usenormalskill == 1) { - imbuement.skills[skillId] = bonus; - } else if (usenormalskill == 2) { - imbuement.stats[skillId] = bonus; - } else if (usenormalskill == 3) { - imbuement.skills[skillId] = bonus; - int32_t chance = 100; - if ((attr = childNode.attribute("chance"))) { - chance = std::min(10000, pugi::cast(attr.value())); - } - - imbuement.skills[skillId - 1] = chance; - } - } else if (strcasecmp(effecttype.c_str(), "damage") == 0) { - if (!(attr = childNode.attribute("combat"))) { - g_logger().warn("Missing combat for imbuement name {}", imbuement.name); - continue; - } - - CombatType_t combatType = getCombatTypeByName(attr.as_string()); - if (combatType == COMBAT_NONE) { - g_logger().warn("Unknown combat type for element {}", attr.as_string()); - continue; - } - - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement.name); - continue; - } - - uint32_t percent = std::min(100, pugi::cast(attr.value())); - - imbuement.combatType = combatType; - imbuement.elementDamage = std::min(100, percent); - } else if (strcasecmp(effecttype.c_str(), "reduction") == 0) { - if (!(attr = childNode.attribute("combat"))) { - g_logger().warn("Missing combat for imbuement name {}", imbuement.name); - continue; - } - - CombatType_t combatType = getCombatTypeByName(attr.as_string()); - if (combatType == COMBAT_NONE) { - g_logger().warn("Unknown combat type for element {}", attr.as_string()); - continue; - } - - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement.name); - continue; - } - - uint32_t percent = std::min(100, pugi::cast(attr.value())); - - imbuement.absorbPercent[combatTypeToIndex(combatType)] = percent; - } else if (strcasecmp(effecttype.c_str(), "speed") == 0) { - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing speed value for imbuement name {}", imbuement.name); - continue; - } - - imbuement.speed = pugi::cast(attr.value()); - } else if (strcasecmp(effecttype.c_str(), "capacity") == 0) { - if (!(attr = childNode.attribute("value"))) { - g_logger().warn("Missing cap value for imbuement name {}", imbuement.name); - continue; - } - - imbuement.capacity = pugi::cast(attr.value()); - } + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement->name); + return false; + } + + uint32_t percent = std::min(100, pugi::cast(attr.value())); + + imbuement->absorbPercent[combatTypeToIndex(combatType)] = static_cast(percent); + } else if (strcasecmp(effecttype.c_str(), "speed") == 0) { + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing speed value for imbuement name {}", imbuement->name); + return false; + } + + imbuement->speed = static_cast(pugi::cast(attr.value())); + } else if (strcasecmp(effecttype.c_str(), "capacity") == 0) { + if (!(attr = childNode.attribute("value"))) { + g_logger().warn("Missing cap value for imbuement name {}", imbuement->name); + return false; + } + + imbuement->capacity = pugi::cast(attr.value()); + } else if (strcasecmp(effecttype.c_str(), "vibrancy") == 0) { + if (!(attr = childNode.attribute("chance"))) { + g_logger().warn("Missing chance value for imbuement name {}", imbuement->name); + return false; } + + imbuement->paralyzeReduction = pugi::cast(attr.value()); } } } - return true; } @@ -320,27 +355,27 @@ bool Imbuements::reload() { return loadFromXml(true); } -BaseImbuement* Imbuements::getBaseByID(uint16_t id) { - auto baseImbuements = std::find_if(basesImbuement.begin(), basesImbuement.end(), [id](const BaseImbuement &groupImbuement) { - return groupImbuement.id == id; +std::shared_ptr Imbuements::getBaseByID(uint16_t id) const { + auto baseImbuements = std::find_if(basesImbuement.begin(), basesImbuement.end(), [id](const auto &groupImbuement) { + return groupImbuement->id == id; }); - return baseImbuements != basesImbuement.end() ? &*baseImbuements : nullptr; + return baseImbuements != basesImbuement.end() ? *baseImbuements : nullptr; } -CategoryImbuement* Imbuements::getCategoryByID(uint16_t id) { - auto categoryImbuements = std::find_if(categoriesImbuement.begin(), categoriesImbuement.end(), [id](const CategoryImbuement &categoryImbuement) { - return categoryImbuement.id == id; +std::shared_ptr Imbuements::getCategoryByID(uint16_t id) const { + auto categoryImbuements = std::find_if(categoriesImbuement.begin(), categoriesImbuement.end(), [id](const auto &categoryImbuement) { + return categoryImbuement->id == id; }); - return categoryImbuements != categoriesImbuement.end() ? &*categoryImbuements : nullptr; + return categoryImbuements != categoriesImbuement.end() ? *categoryImbuements : nullptr; } -std::vector Imbuements::getImbuements(std::shared_ptr player, std::shared_ptr item) { - std::vector imbuements; +std::vector> Imbuements::getImbuements(const std::shared_ptr &player, const std::shared_ptr &item) const { + std::vector> imbuements; for (auto &[key, value] : imbuementMap) { - Imbuement* imbuement = &value; + const std::shared_ptr &imbuement = value; if (!imbuement) { continue; } @@ -354,7 +389,7 @@ std::vector Imbuements::getImbuements(std::shared_ptr player } // Send only the imbuements registered on item (in items.xml) to the imbuement window - const CategoryImbuement* categoryImbuement = getCategoryByID(imbuement->getCategory()); + const std::shared_ptr &categoryImbuement = getCategoryByID(imbuement->getCategory()); if (!item->hasImbuementType(static_cast(categoryImbuement->id), imbuement->getBaseID())) { continue; } @@ -364,7 +399,7 @@ std::vector Imbuements::getImbuements(std::shared_ptr player continue; } - imbuements.push_back(imbuement); + imbuements.emplace_back(imbuement); } return imbuements; diff --git a/src/creatures/players/imbuements/imbuements.hpp b/src/creatures/players/imbuements/imbuements.hpp index 265e813aa1c..d992b3bd06c 100644 --- a/src/creatures/players/imbuements/imbuements.hpp +++ b/src/creatures/players/imbuements/imbuements.hpp @@ -17,7 +17,11 @@ class Player; class Item; -class Imbuement; +enum UseSkillMode : uint8_t { + NormalSkill = 1, + MagicLevel = 2, + SpecialSkill = 3 +}; struct BaseImbuement { BaseImbuement(uint16_t initId, std::string initName, uint32_t initPrice, uint32_t initProtectionPrice, uint32_t initRemoveCost, uint32_t initDuration, uint8_t initPercent) : @@ -56,23 +60,27 @@ class Imbuements { return inject(); } - Imbuement* getImbuement(uint16_t id); + std::shared_ptr getImbuement(uint16_t id) const; + std::shared_ptr getBaseByID(uint16_t id) const; + std::shared_ptr getCategoryByID(uint16_t id) const; - BaseImbuement* getBaseByID(uint16_t id); - CategoryImbuement* getCategoryByID(uint16_t id); - std::vector getImbuements(std::shared_ptr player, std::shared_ptr item); + std::vector> getImbuements(const std::shared_ptr &player, const std::shared_ptr &item) const; protected: friend class Imbuement; bool loaded = false; private: - std::map imbuementMap; + uint32_t runningid = 0; - std::vector basesImbuement; - std::vector categoriesImbuement; + std::map> imbuementMap; + std::vector> basesImbuement; + std::vector> categoriesImbuement; - uint32_t runningid = 0; + bool processBaseNode(const pugi::xml_node &baseNode); + bool processCategoryNode(const pugi::xml_node &categoryNode); + bool processImbuementNode(const pugi::xml_node &imbuementNode); + bool processImbuementChildNodes(const pugi::xml_node &imbuementNode, const std::shared_ptr &imbuement); }; constexpr auto g_imbuements = Imbuements::getInstance; @@ -94,7 +102,7 @@ class Imbuement { return storage; } - bool isPremium() { + bool isPremium() const { return premium; } std::string getName() const { @@ -116,7 +124,7 @@ class Imbuement { return items; } - uint16_t getIconID() { + uint16_t getIconID() const { return icon + (baseid - 1); } @@ -124,11 +132,11 @@ class Imbuement { int32_t stats[STAT_LAST + 1] = {}; int32_t skills[SKILL_LAST + 1] = {}; int32_t speed = 0; + int32_t paralyzeReduction = 0; uint32_t capacity = 0; int16_t absorbPercent[COMBAT_COUNT] = {}; int16_t elementDamage = 0; SoundEffect_t soundEffect = SoundEffect_t::SILENCE; - CombatType_t combatType = COMBAT_NONE; protected: @@ -139,7 +147,7 @@ class Imbuement { bool premium = false; uint32_t storage = 0; uint16_t id, baseid, category = 0; - std::string name, description, subgroup = ""; + std::string name, description, subgroup; std::vector> items; }; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 925086f2b53..fdba2a7d931 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -583,7 +583,7 @@ void Player::updateInventoryImbuement() { // Imbuement from imbuementInfo, this variable reduces code complexity auto imbuement = imbuementInfo.imbuement; // Get the category of the imbuement - const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); + const auto &categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); // Parent of the imbued item auto parent = item->getParent(); bool isInBackpack = parent && parent->getContainer(); @@ -1453,7 +1453,7 @@ void Player::sendHouseWindow(std::shared_ptr house, uint32_t listId) cons } } -void Player::onApplyImbuement(Imbuement* imbuement, std::shared_ptr item, uint8_t slot, bool protectionCharm) { +void Player::onApplyImbuement(std::shared_ptr imbuement, std::shared_ptr item, uint8_t slot, bool protectionCharm) { if (!imbuement || !item) { return; } @@ -1474,7 +1474,7 @@ void Player::onApplyImbuement(Imbuement* imbuement, std::shared_ptr item, } } - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); if (!baseImbuement) { return; } @@ -1541,7 +1541,7 @@ void Player::onClearImbuement(std::shared_ptr item, uint8_t slot) { return; } - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); if (!baseImbuement) { return; } @@ -6383,7 +6383,7 @@ uint16_t Player::getFreeBackpackSlots() const { return counter; } -void Player::addItemImbuementStats(const Imbuement* imbuement) { +void Player::addItemImbuementStats(const std::shared_ptr imbuement) { bool requestUpdate = false; // Check imbuement skills for (int32_t skill = SKILL_FIRST; skill <= SKILL_LAST; ++skill) { @@ -6418,7 +6418,7 @@ void Player::addItemImbuementStats(const Imbuement* imbuement) { } } -void Player::removeItemImbuementStats(const Imbuement* imbuement) { +void Player::removeItemImbuementStats(const std::shared_ptr imbuement) { if (!imbuement) { return; } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index da4c0cec9e8..56fe064e955 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1566,7 +1566,7 @@ class Player final : public Creature, public Cylinder, public Bankable { } } // Imbuements - void onApplyImbuement(Imbuement* imbuement, std::shared_ptr item, uint8_t slot, bool protectionCharm); + void onApplyImbuement(std::shared_ptr imbuement, std::shared_ptr item, uint8_t slot, bool protectionCharm); void onClearImbuement(std::shared_ptr item, uint8_t slot); void openImbuementWindow(std::shared_ptr item); void sendImbuementResult(const std::string message) { @@ -1853,8 +1853,8 @@ class Player final : public Creature, public Cylinder, public Bankable { void setTraining(bool value); - void addItemImbuementStats(const Imbuement* imbuement); - void removeItemImbuementStats(const Imbuement* imbuement); + void addItemImbuementStats(const std::shared_ptr imbuement); + void removeItemImbuementStats(const std::shared_ptr imbuement); void updateImbuementTrackerStats() const; bool isUIExhausted(uint32_t exhaustionTime = 250) const; diff --git a/src/game/game.cpp b/src/game/game.cpp index d731bd766b6..93db62e9267 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5805,7 +5805,7 @@ void Game::playerApplyImbuement(uint32_t playerId, uint16_t imbuementid, uint8_t return; } - Imbuement* imbuement = g_imbuements().getImbuement(imbuementid); + const auto &imbuement = g_imbuements().getImbuement(imbuementid); if (!imbuement) { return; } diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 2d1ba9301c5..520be1613c0 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -865,7 +865,7 @@ void ItemParse::parseImbuement(const std::string &tmpStrValue, pugi::xml_node at continue; } } else { - g_logger().warn("[ParseImbuement::initParseImbuement] - Unknown type: {}", valueAttribute.as_string()); + g_logger().warn("[ParseImbuement::initParseImbuement] - Unknown type: {}, item: {}", valueAttribute.as_string(), itemType.id); } } } diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index 7859a77fe9c..63fd09d2cc7 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -244,7 +244,8 @@ const phmap::flat_hash_map ImbuementsTypeMap = { { "skillboost shielding", IMBUEMENT_SKILLBOOST_SHIELDING }, { "skillboost distance", IMBUEMENT_SKILLBOOST_DISTANCE }, { "skillboost magic level", IMBUEMENT_SKILLBOOST_MAGIC_LEVEL }, - { "increase capacity", IMBUEMENT_INCREASE_CAPACITY } + { "increase capacity", IMBUEMENT_INCREASE_CAPACITY }, + { "vibrancy", IMBUEMENT_VIBRANCY_PARALYZE } }; class ItemParse : public Items { diff --git a/src/items/item.cpp b/src/items/item.cpp index 81d24a790e7..3c39c7fa992 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -106,13 +106,13 @@ void Item::addImbuement(uint8_t slot, uint16_t imbuementId, uint32_t duration) { } // Get imbuement by the id - const Imbuement* imbuement = g_imbuements().getImbuement(imbuementId); + const auto &imbuement = g_imbuements().getImbuement(imbuementId); if (!imbuement) { return; } // Get category imbuement for acess category id - const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); + const auto &categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); if (!hasImbuementType(static_cast(categoryImbuement->id), imbuement->getBaseID())) { return; } @@ -131,7 +131,7 @@ bool Item::hasImbuementCategoryId(uint16_t categoryId) const { for (uint8_t slotid = 0; slotid < getImbuementSlot(); slotid++) { ImbuementInfo imbuementInfo; if (getImbuementInfo(slotid, &imbuementInfo)) { - if (const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuementInfo.imbuement->getCategory()); + if (const auto &categoryImbuement = g_imbuements().getCategoryByID(imbuementInfo.imbuement->getCategory()); categoryImbuement->id == categoryId) { return true; } @@ -1424,7 +1424,7 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr item /*= nullptr continue; } - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); if (!baseImbuement) { continue; } @@ -1914,7 +1914,7 @@ std::string Item::parseImbuementDescription(std::shared_ptr item) { continue; } - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); if (!baseImbuement) { continue; } diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index f044d4b5248..07b386ad701 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -267,7 +267,8 @@ enum ImbuementTypes_t : int64_t { IMBUEMENT_SKILLBOOST_SHIELDING = 14, IMBUEMENT_SKILLBOOST_DISTANCE = 15, IMBUEMENT_SKILLBOOST_MAGIC_LEVEL = 16, - IMBUEMENT_INCREASE_CAPACITY = 17 + IMBUEMENT_INCREASE_CAPACITY = 17, + IMBUEMENT_VIBRANCY_PARALYZE = 18 }; enum class ContainerCategory_t : uint8_t { @@ -609,6 +610,6 @@ enum ItemParseAttributes_t { }; struct ImbuementInfo { - Imbuement* imbuement; + std::shared_ptr imbuement; uint32_t duration = 0; }; diff --git a/src/lua/functions/items/imbuement_functions.cpp b/src/lua/functions/items/imbuement_functions.cpp index f24a3f564d7..f2e73805677 100644 --- a/src/lua/functions/items/imbuement_functions.cpp +++ b/src/lua/functions/items/imbuement_functions.cpp @@ -16,7 +16,7 @@ int ImbuementFunctions::luaCreateImbuement(lua_State* L) { // Imbuement(id) uint16_t imbuementId = getNumber(L, 2); - Imbuement* imbuement = g_imbuements().getImbuement(imbuementId); + const auto &imbuement = g_imbuements().getImbuement(imbuementId); if (imbuement) { pushUserdata(L, imbuement); @@ -29,7 +29,7 @@ int ImbuementFunctions::luaCreateImbuement(lua_State* L) { int ImbuementFunctions::luaImbuementGetName(lua_State* L) { // imbuement:getName() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (imbuement) { pushString(L, imbuement->getName()); } else { @@ -40,7 +40,7 @@ int ImbuementFunctions::luaImbuementGetName(lua_State* L) { int ImbuementFunctions::luaImbuementGetId(lua_State* L) { // imbuement:getId() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (imbuement) { lua_pushnumber(L, imbuement->getID()); } else { @@ -51,7 +51,7 @@ int ImbuementFunctions::luaImbuementGetId(lua_State* L) { int ImbuementFunctions::luaImbuementGetItems(lua_State* L) { // imbuement:getItems() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (!imbuement) { lua_pushnil(L); return 1; @@ -72,13 +72,13 @@ int ImbuementFunctions::luaImbuementGetItems(lua_State* L) { int ImbuementFunctions::luaImbuementGetBase(lua_State* L) { // imbuement:getBase() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (!imbuement) { lua_pushnil(L); return 1; } - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); if (!baseImbuement) { lua_pushnil(L); return 1; @@ -97,13 +97,13 @@ int ImbuementFunctions::luaImbuementGetBase(lua_State* L) { int ImbuementFunctions::luaImbuementGetCategory(lua_State* L) { // imbuement:getCategory() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (!imbuement) { lua_pushnil(L); return 1; } uint16_t categoryId = imbuement->getCategory(); - const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(categoryId); + const auto &categoryImbuement = g_imbuements().getCategoryByID(categoryId); if (categoryImbuement) { lua_createtable(L, 0, 2); @@ -118,7 +118,7 @@ int ImbuementFunctions::luaImbuementGetCategory(lua_State* L) { int ImbuementFunctions::luaImbuementIsPremium(lua_State* L) { // imbuement:isPremium() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (!imbuement) { lua_pushnil(L); return 1; @@ -130,7 +130,7 @@ int ImbuementFunctions::luaImbuementIsPremium(lua_State* L) { int ImbuementFunctions::luaImbuementGetElementDamage(lua_State* L) { // imbuement:getElementDamage() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (imbuement) { lua_pushnumber(L, imbuement->elementDamage); } else { @@ -141,7 +141,7 @@ int ImbuementFunctions::luaImbuementGetElementDamage(lua_State* L) { int ImbuementFunctions::luaImbuementGetCombatType(lua_State* L) { // imbuement:getCombatType() - Imbuement* imbuement = getUserdata(L, 1); + const auto &imbuement = getUserdata(L, 1); if (imbuement) { lua_pushnumber(L, imbuement->combatType); } else { diff --git a/src/lua/functions/items/imbuement_functions.hpp b/src/lua/functions/items/imbuement_functions.hpp index 516fe91a3b1..6e9457b8a4c 100644 --- a/src/lua/functions/items/imbuement_functions.hpp +++ b/src/lua/functions/items/imbuement_functions.hpp @@ -14,7 +14,7 @@ class ImbuementFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Imbuement", "", ImbuementFunctions::luaCreateImbuement); + registerSharedClass(L, "Imbuement", "", ImbuementFunctions::luaCreateImbuement); registerMetaMethod(L, "Imbuement", "__eq", ImbuementFunctions::luaUserdataCompare); registerMethod(L, "Imbuement", "getName", ImbuementFunctions::luaImbuementGetName); diff --git a/src/lua/functions/items/item_functions.cpp b/src/lua/functions/items/item_functions.cpp index 71ab040f47d..a72c1ca6282 100644 --- a/src/lua/functions/items/item_functions.cpp +++ b/src/lua/functions/items/item_functions.cpp @@ -776,7 +776,7 @@ int ItemFunctions::luaItemGetImbuement(lua_State* L) { continue; } - Imbuement* imbuement = imbuementInfo.imbuement; + const auto &imbuement = imbuementInfo.imbuement; if (!imbuement) { continue; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d924e0bfb0e..e36188519cf 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -7569,9 +7569,9 @@ void ProtocolGame::AddOutfit(NetworkMessage &msg, const Outfit_t &outfit, bool a } void ProtocolGame::addImbuementInfo(NetworkMessage &msg, uint16_t imbuementId) const { - Imbuement* imbuement = g_imbuements().getImbuement(imbuementId); - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); - const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); + const auto &imbuement = g_imbuements().getImbuement(imbuementId); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); + const auto &categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); msg.add(imbuementId); msg.addString(baseImbuement->name + " " + imbuement->getName(), "ProtocolGame::addImbuementInfo - baseImbuement->name + " @@ -7628,11 +7628,11 @@ void ProtocolGame::openImbuementWindow(std::shared_ptr item) { msg.add(g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID())->removeCost); } - std::vector imbuements = g_imbuements().getImbuements(player, item); + std::vector> imbuements = g_imbuements().getImbuements(player, item); phmap::flat_hash_map needItems; msg.add(imbuements.size()); - for (const Imbuement* imbuement : imbuements) { + for (const auto &imbuement : imbuements) { addImbuementInfo(msg, imbuement->getID()); const auto items = imbuement->getItems(); @@ -8116,7 +8116,7 @@ void ProtocolGame::sendInventoryImbuements(const std::mapgetBaseID()); + const auto &baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); msg.addByte(0x01); msg.addString(baseImbuement->name + " " + imbuement->getName(), "ProtocolGame::sendInventoryImbuements - baseImbuement->name + " " + imbuement->getName()"); @@ -8129,7 +8129,7 @@ void ProtocolGame::sendInventoryImbuements(const std::maphasCondition(CONDITION_INFIGHT); // Get the category of the imbuement - const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); + const auto &categoryImbuement = g_imbuements().getCategoryByID(imbuement->getCategory()); // Parent of the imbued item auto parent = item->getParent(); // If the imbuement is aggressive and the player is not in fight mode or is in a protection zone, or the item is in a container, ignore it. diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 0d8b39a485d..c81bad7003c 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -869,7 +869,8 @@ const ImbuementTypeNames imbuementTypeNames = { { "skillboost shielding", IMBUEMENT_SKILLBOOST_SHIELDING }, { "skillboost distance", IMBUEMENT_SKILLBOOST_DISTANCE }, { "skillboost magic level", IMBUEMENT_SKILLBOOST_MAGIC_LEVEL }, - { "increase capacity", IMBUEMENT_INCREASE_CAPACITY } + { "increase capacity", IMBUEMENT_INCREASE_CAPACITY }, + { "vibrancy", IMBUEMENT_VIBRANCY_PARALYZE } }; /**