From 84261ff2861a968d635357366d89801cdc280fbe Mon Sep 17 00:00:00 2001 From: Shauren Date: Fri, 22 May 2026 01:19:04 +0200 Subject: [PATCH] Core/Items: Fixed item level and stat calculation --- src/server/game/Entities/Item/Item.cpp | 12 +-- src/server/game/Entities/Player/Player.cpp | 102 ++++++++++++++------- 2 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index e35a61b7ef..51606786bd 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -2295,13 +2295,13 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon else if (Optional levels = sDB2Manager.GetContentTuningData(bonusData.ContentTuningId, {}, true)) level = std::min(std::max(int16(level), levels->MinLevel), levels->MaxLevel); - itemLevel = uint32(sDB2Manager.GetCurveValueAt(bonusData.PlayerLevelToItemLevelCurveId, level)); + itemLevel = uint32(std::round(sDB2Manager.GetCurveValueAt(bonusData.PlayerLevelToItemLevelCurveId, level))); } itemLevel += bonusData.ItemLevelBonus; } else - itemLevel = bonusData.ItemLevelOffset + uint32(sDB2Manager.GetCurveValueAt(bonusData.ItemLevelOffsetCurveId, bonusData.ItemLevelOffsetItemLevel)); + itemLevel = bonusData.ItemLevelOffset + uint32(std::round(sDB2Manager.GetCurveValueAt(bonusData.ItemLevelOffsetCurveId, bonusData.ItemLevelOffsetItemLevel))); for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i) itemLevel += bonusData.GemItemLevelBonus[i]; @@ -2323,17 +2323,17 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon int32 currentBuild = ClientBuild::GetMinorMajorBugfixVersionForBuild(currentRealm->Build); // apply all squishes between items_squish and server_squish - for (uint32 squishId = bonusData.ItemSquishEraID; squishId < sItemSquishEraStore.GetNumRows(); ++squishId) + for (uint32 squishId = bonusData.ItemSquishEraID + 1; squishId < sItemSquishEraStore.GetNumRows(); ++squishId) { ItemSquishEraEntry const* squish = sItemSquishEraStore.LookupEntry(squishId); - if (!squish) + if (!squish || squish->Flags & 0x1) continue; if (squish->Patch > currentBuild) break; if (squish->CurveID) - itemLevel = uint32(sDB2Manager.GetCurveValueAt(squish->CurveID, itemLevel)); + itemLevel = uint32(std::round(sDB2Manager.GetCurveValueAt(squish->CurveID, itemLevel))); } } } @@ -2367,7 +2367,7 @@ float Item::GetItemStatValue(uint32 index, Player const* owner) const { float statValue = float(_bonusData.StatPercentEditor[index] * randomPropPoints) * 0.0001f; if (GtItemSocketCostPerLevelEntry const* gtCost = sItemSocketCostPerLevelGameTable.GetRow(itemLevel)) - statValue -= float(int32(_bonusData.ItemStatSocketCostMultiplier[index] * gtCost->SocketCost)); + statValue -= float(_bonusData.ItemStatSocketCostMultiplier[index] * gtCost->SocketCost); return statValue; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 84d1d52264..d71c6d642f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7970,6 +7970,44 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) if (val == 0) continue; + // apply stat multiplier + switch (statType) + { + case ITEM_MOD_STAMINA: + if (GtStaminaMultByILvl const* staminaMult = sStaminaMultByILvlGameTable.GetRow(itemLevel)) + val *= GetIlvlStatMultiplier(staminaMult, proto->GetInventoryType()); + break; + case ITEM_MOD_DEFENSE_SKILL_RATING: + case ITEM_MOD_DODGE_RATING: + case ITEM_MOD_PARRY_RATING: + case ITEM_MOD_BLOCK_RATING: + case ITEM_MOD_HIT_MELEE_RATING: + case ITEM_MOD_HIT_RANGED_RATING: + case ITEM_MOD_HIT_SPELL_RATING: + case ITEM_MOD_CRIT_MELEE_RATING: + case ITEM_MOD_CRIT_RANGED_RATING: + case ITEM_MOD_CRIT_SPELL_RATING: + case ITEM_MOD_HIT_RATING: + case ITEM_MOD_CRIT_RATING: + case ITEM_MOD_HIT_TAKEN_RATING: + case ITEM_MOD_CRIT_TAKEN_RATING: + case ITEM_MOD_RESILIENCE_RATING: + case ITEM_MOD_HASTE_RATING: + case ITEM_MOD_EXPERTISE_RATING: + case ITEM_MOD_VERSATILITY: + case ITEM_MOD_MASTERY_RATING: + case ITEM_MOD_CR_SPEED: + case ITEM_MOD_CR_LIFESTEAL: + case ITEM_MOD_CR_AVOIDANCE: + case ITEM_MOD_CR_STURDINESS: + val *= combatRatingMultiplier; + break; + default: + break; + } + + val = std::round(val); + switch (statType) { case ITEM_MOD_MANA: @@ -7995,42 +8033,38 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) UpdateStatBuffMod(STAT_SPIRIT); break; case ITEM_MOD_STAMINA: //modify stamina - { - if (GtStaminaMultByILvl const* staminaMult = sStaminaMultByILvlGameTable.GetRow(itemLevel)) - val = int32(val * GetIlvlStatMultiplier(staminaMult, proto->GetInventoryType())); HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); UpdateStatBuffMod(STAT_STAMINA); break; - } case ITEM_MOD_DEFENSE_SKILL_RATING: - ApplyRatingMod(CR_DEFENSE_SKILL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply); break; case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_DODGE, int32(val), apply); break; case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_PARRY, int32(val), apply); break; case ITEM_MOD_BLOCK_RATING: - ApplyRatingMod(CR_BLOCK, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_BLOCK, int32(val), apply); break; case ITEM_MOD_HIT_MELEE_RATING: - ApplyRatingMod(CR_HIT_MELEE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_HIT_MELEE, int32(val), apply); break; case ITEM_MOD_HIT_RANGED_RATING: - ApplyRatingMod(CR_HIT_RANGED, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_HIT_RANGED, int32(val), apply); break; case ITEM_MOD_HIT_SPELL_RATING: - ApplyRatingMod(CR_HIT_SPELL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_HIT_SPELL, int32(val), apply); break; case ITEM_MOD_CRIT_MELEE_RATING: - ApplyRatingMod(CR_CRIT_MELEE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply); break; case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply); break; case ITEM_MOD_CRIT_SPELL_RATING: - ApplyRatingMod(CR_CRIT_SPELL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply); break; // case ITEM_MOD_HIT_TAKEN_MELEE_RATING: // ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply); @@ -8060,14 +8094,14 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply); break; case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_HIT_RANGED, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_HIT_SPELL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_HIT_MELEE, int32(val), apply); + ApplyRatingMod(CR_HIT_RANGED, int32(val), apply); + ApplyRatingMod(CR_HIT_SPELL, int32(val), apply); break; case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_CRIT_RANGED, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_CRIT_SPELL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply); + ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply); + ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply); break; // case ITEM_MOD_HIT_TAKEN_RATING: // Unused since 3.3.5 // ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply); @@ -8080,15 +8114,15 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply); // break; case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE, int32(val), apply); break; case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_HASTE_RANGED, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_HASTE_SPELL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply); + ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply); + ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply); break; case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_EXPERTISE, int32(val), apply); break; case ITEM_MOD_ATTACK_POWER: HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply); @@ -8098,9 +8132,9 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); break; case ITEM_MOD_VERSATILITY: - ApplyRatingMod(CR_VERSATILITY_DAMAGE_DONE, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_VERSATILITY_DAMAGE_TAKEN, int32(val * combatRatingMultiplier), apply); - ApplyRatingMod(CR_VERSATILITY_HEALING_DONE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_VERSATILITY_DAMAGE_DONE, int32(val), apply); + ApplyRatingMod(CR_VERSATILITY_DAMAGE_TAKEN, int32(val), apply); + ApplyRatingMod(CR_VERSATILITY_HEALING_DONE, int32(val), apply); break; case ITEM_MOD_MANA_REGENERATION: ApplyManaRegenBonus(int32(val), apply); @@ -8115,10 +8149,10 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) ApplyHealthRegenBonus(int32(val), apply); break; case ITEM_MOD_SPELL_PENETRATION: - ApplySpellPenetrationBonus(val, apply); + ApplySpellPenetrationBonus(int32(val), apply); break; case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_MASTERY, int32(val), apply); break; case ITEM_MOD_EXTRA_ARMOR: HandleStatFlatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(val), apply); @@ -8151,16 +8185,16 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) ApplyRatingMod(CR_CORRUPTION_RESISTANCE, int32(val), apply); break; case ITEM_MOD_CR_SPEED: - ApplyRatingMod(CR_SPEED, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_SPEED, int32(val), apply); break; case ITEM_MOD_CR_LIFESTEAL: - ApplyRatingMod(CR_LIFESTEAL, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_LIFESTEAL, int32(val), apply); break; case ITEM_MOD_CR_AVOIDANCE: - ApplyRatingMod(CR_AVOIDANCE, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_AVOIDANCE, int32(val), apply); break; case ITEM_MOD_CR_STURDINESS: - ApplyRatingMod(CR_STURDINESS, int32(val * combatRatingMultiplier), apply); + ApplyRatingMod(CR_STURDINESS, int32(val), apply); break; case ITEM_MOD_AGI_STR_INT: HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);