diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 7276bea19d1..a5919702825 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -20,6 +20,7 @@ #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" #include "map/spectators.hpp" +#include "items/weapons/weapons.hpp" int32_t Monster::despawnRange; int32_t Monster::despawnRadius; @@ -2660,3 +2661,18 @@ void Monster::onExecuteAsyncTasks() { updateIdleStatus(); } } + +std::map Monster::calculateElementalDamage(CombatType_t weaponElement, int32_t baseDamage) const { + std::map damageByElement; + // Start with the base damage assigned to the weapon's element type + int32_t elementDamage = baseDamage; + // Check the monster's resistance/weakness for the element type + auto it = mType->info.elementMap.find(weaponElement); + if (it != mType->info.elementMap.end()) { + int32_t elementMod = it->second; // Monster's resistance or weakness + elementDamage = static_cast(std::round(elementDamage * ((100 - elementMod) / 100.0))); + } + // Add the calculated damage to the result map + damageByElement[weaponElement] = elementDamage; + return damageByElement; +} diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index b8a6087acc1..d373fb373f1 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -221,6 +221,11 @@ class Monster final : public Creature { void setDead(bool isDead); + std::map calculateElementalDamage( + CombatType_t weaponElement, + int32_t baseDamage + ) const; + protected: void onExecuteAsyncTasks() override; diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 2ec09945f36..814970a8c6b 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -17,6 +17,7 @@ #include "lib/di/container.hpp" #include "lua/scripts/scripts.hpp" #include "lua/global/lua_variant.hpp" +#include "creatures/monsters/monster.hpp" #include "creatures/players/player.hpp" Weapons::Weapons() = default; @@ -258,11 +259,25 @@ void Weapon::internalUseWeapon(const std::shared_ptr &player, const std: damage.origin = ORIGIN_MELEE; } + // Define the primary and secondary damage types damage.primary.type = params.combatType; - damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier) / 100; damage.secondary.type = getElementType(); - // Cleave damage + // Step 1: Calculate total damage + const int32_t totalDamage = (getWeaponDamage(player, target, item) * damageModifier) / 100; + if (damage.secondary.type != COMBAT_NONE) { + // Step 2: Use the helper function to distribute the damage + auto [physicalDamage, elementalDamage] = calculateDamageDistribution(target, totalDamage); + // Step 3: Assign the calculated values to the damage object + damage.primary.value = physicalDamage; + damage.secondary.value = elementalDamage; + } else { + // Step 2: Assign the total damage to the primary value + damage.primary.value = totalDamage; + damage.secondary.value = 0; + } + + // Apply cleave adjustments if applicable uint16_t damagePercent = 100; if (cleavePercent != 0) { damage.extension = true; @@ -271,16 +286,11 @@ void Weapon::internalUseWeapon(const std::shared_ptr &player, const std: damage.exString += ", "; } damage.exString += "cleave damage"; + damage.primary.value = (damage.primary.value * damagePercent) / 100; + damage.secondary.value = (damage.secondary.value * damagePercent) / 100; } - if (damage.secondary.type == COMBAT_NONE) { - damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier / 100) * damagePercent / 100; - damage.secondary.value = 0; - } else { - damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier / 100) * damagePercent / 100; - damage.secondary.value = (getElementDamage(player, target, item) * damageModifier / 100) * damagePercent / 100; - } - + // Handle chain system if (g_configManager().getBoolean(TOGGLE_CHAIN_SYSTEM) && params.chainCallback) { m_combat->doCombatChain(player, target, params.aggressive); g_logger().debug("Weapon::internalUseWeapon - Chain callback executed."); @@ -484,6 +494,43 @@ std::shared_ptr Weapon::getCombat() { return m_combat; } +std::pair Weapon::calculateDamageDistribution( + const std::shared_ptr &target, + int32_t totalDamage +) const { + // Default damage distribution percentages + constexpr float DEFAULT_ELEMENTAL_PERCENTAGE = 0.666f; // 2/3 elemental damage + constexpr float DEFAULT_PHYSICAL_PERCENTAGE = 1.0f - DEFAULT_ELEMENTAL_PERCENTAGE; + // Initialize damage percentages + float elementalPercentage = DEFAULT_ELEMENTAL_PERCENTAGE; + float physicalPercentage = DEFAULT_PHYSICAL_PERCENTAGE; + // Check if the target is a player + const auto &targetPlayer = target ? target->getPlayer() : nullptr; + if (targetPlayer) { + // Players have no weaknesses, damage is split 50/50 + elementalPercentage = 0.5f; + physicalPercentage = 0.5f; + } + // Calculate base physical and elemental damage + int32_t physicalDamage = static_cast(totalDamage * physicalPercentage); + int32_t elementalDamage = static_cast(totalDamage * elementalPercentage); + // Apply resistance/weakness modifiers for monsters only + if (!targetPlayer && target) { + const auto &targetMonster = target->getMonster(); + // Use the Monster::calculateElementalDamage function + if (targetMonster) { + auto elementType = getElementType(); + auto elementDamageMap = targetMonster->calculateElementalDamage(elementType, elementalDamage); + auto it = elementDamageMap.find(elementType); + if (it != elementDamageMap.end()) { + elementalDamage = it->second; // Update elemental damage with calculated value + } + } + } + // Return the calculated damage distribution + return { physicalDamage, elementalDamage }; +} + WeaponMelee::WeaponMelee() { // Add combat type and blocked attributes to the weapon params.blockedByArmor = true; @@ -629,7 +676,7 @@ int32_t WeaponMelee::getWeaponDamage(const std::shared_ptr &player, cons return -maxValue; } - return -normal_random(minValue, (maxValue * static_cast(player->getVocation()->meleeDamageMultiplier))); + return -normal_random(minValue, maxValue); } WeaponDistance::WeaponDistance() { diff --git a/src/items/weapons/weapons.hpp b/src/items/weapons/weapons.hpp index dca7d814033..9b8f9ba31d0 100644 --- a/src/items/weapons/weapons.hpp +++ b/src/items/weapons/weapons.hpp @@ -206,6 +206,10 @@ class Weapon { std::shared_ptr getCombat(); bool calculateSkillFormula(const std::shared_ptr &player, int32_t &attackSkill, int32_t &attackValue, float &attackFactor, int16_t &elementAttack, CombatDamage &damage, bool useCharges = false) const; + std::pair calculateDamageDistribution( + const std::shared_ptr &target, + int32_t totalDamage + ) const; LuaScriptInterface* getScriptInterface() const; bool loadScriptId();