diff options
author | Ovahlord <dreadkiller@gmx.de> | 2025-04-11 23:11:12 +0200 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2025-04-11 23:11:12 +0200 |
commit | bc5698790af1728c6ef7f81d86aa3a00e3c97dd7 (patch) | |
tree | 1b6bddb14284c8ad612f161f0fa91bb6dc586f6a | |
parent | e3773a1fb60369016d28bad879b862a26390be21 (diff) |
Core/Items: restore Heirloom stat scaling
-rw-r--r-- | sql/updates/hotfixes/cata_classic/2025_04_11_01_hotfixes.sql | 45 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 6 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2LoadInfo.h | 8 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 13 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 6 | ||||
-rw-r--r-- | src/server/game/Entities/Item/Item.cpp | 51 | ||||
-rw-r--r-- | src/server/game/Entities/Item/Item.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 142 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 7 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 4 |
11 files changed, 247 insertions, 40 deletions
diff --git a/sql/updates/hotfixes/cata_classic/2025_04_11_01_hotfixes.sql b/sql/updates/hotfixes/cata_classic/2025_04_11_01_hotfixes.sql new file mode 100644 index 00000000000..5d9656dcedb --- /dev/null +++ b/sql/updates/hotfixes/cata_classic/2025_04_11_01_hotfixes.sql @@ -0,0 +1,45 @@ +-- +-- Table structure for table `scaling_stat_distribution` +-- + +DROP TABLE IF EXISTS `scaling_stat_distribution`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `scaling_stat_distribution` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `PlayerLevelToItemLevelCurveID` smallint unsigned NOT NULL DEFAULT '0', + `Minlevel` int NOT NULL DEFAULT '0', + `Maxlevel` int NOT NULL DEFAULT '0', + `Bonus1` int NOT NULL DEFAULT '0', + `Bonus2` int NOT NULL DEFAULT '0', + `Bonus3` int NOT NULL DEFAULT '0', + `Bonus4` int NOT NULL DEFAULT '0', + `Bonus5` int NOT NULL DEFAULT '0', + `Bonus6` int NOT NULL DEFAULT '0', + `Bonus7` int NOT NULL DEFAULT '0', + `Bonus8` int NOT NULL DEFAULT '0', + `Bonus9` int NOT NULL DEFAULT '0', + `Bonus10` int NOT NULL DEFAULT '0', + `StatID1` int NOT NULL DEFAULT '0', + `StatID2` int NOT NULL DEFAULT '0', + `StatID3` int NOT NULL DEFAULT '0', + `StatID4` int NOT NULL DEFAULT '0', + `StatID5` int NOT NULL DEFAULT '0', + `StatID6` int NOT NULL DEFAULT '0', + `StatID7` int NOT NULL DEFAULT '0', + `StatID8` int NOT NULL DEFAULT '0', + `StatID9` int NOT NULL DEFAULT '0', + `StatID10` int NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `scaling_stat_distribution` +-- + +LOCK TABLES `scaling_stat_distribution` WRITE; +/*!40000 ALTER TABLE `scaling_stat_distribution` DISABLE KEYS */; +/*!40000 ALTER TABLE `scaling_stat_distribution` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index a0e2bf66dd1..9fb645bc571 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -1100,9 +1100,9 @@ void HotfixDatabaseConnection::DoPrepareStatements() PREPARE_MAX_ID_STMT(HOTFIX_SEL_REWARD_PACK_X_ITEM, "SELECT MAX(ID) + 1 FROM reward_pack_x_item", CONNECTION_SYNCH); // ScalingStatDistribution.db2 - PrepareStatement(HOTFIX_SEL_SCALING_STAT_DISTRIBUTION, "SELECT ID, StatID1, StatID2, StatID3, StatID4, StatID5, StatID6, StatID7, StatID8, " - "StatID9, StatID10, Bonus1, Bonus2, Bonus3, Bonus4, Bonus5, Bonus6, Bonus7, Bonus8, Bonus9, Bonus10, Maxlevel FROM scaling_stat_distribution" - " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PrepareStatement(HOTFIX_SEL_SCALING_STAT_DISTRIBUTION, "SELECT ID, PlayerLevelToItemLevelCurveID, Minlevel, Maxlevel, Bonus1, Bonus2, Bonus3, " + "Bonus4, Bonus5, Bonus6, Bonus7, Bonus8, Bonus9, Bonus10, StatID1, StatID2, StatID3, StatID4, StatID5, StatID6, StatID7, StatID8, StatID9, " + "StatID10 FROM scaling_stat_distribution WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_SCALING_STAT_DISTRIBUTION, "SELECT MAX(ID) + 1 FROM scaling_stat_distribution", CONNECTION_SYNCH); // ScalingStatValues.db2 diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index ae072a81863..b15c0407000 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -3713,9 +3713,12 @@ struct RewardPackXItemLoadInfo struct ScalingStatDistributionLoadInfo { - static constexpr DB2FieldMeta Fields[22] = + static constexpr DB2FieldMeta Fields[24] = { { false, FT_INT, "ID" }, + { false, FT_SHORT, "PlayerLevelToItemLevelCurveID" }, + { true, FT_INT, "Minlevel" }, + { true, FT_INT, "Maxlevel" }, { true, FT_INT, "StatID1" }, { true, FT_INT, "StatID2" }, { true, FT_INT, "StatID3" }, @@ -3736,10 +3739,9 @@ struct ScalingStatDistributionLoadInfo { true, FT_INT, "Bonus8" }, { true, FT_INT, "Bonus9" }, { true, FT_INT, "Bonus10" }, - { true, FT_INT, "Maxlevel" }, }; - static constexpr DB2LoadInfo Instance{ Fields, 22, &ScalingStatDistributionMeta::Instance, HOTFIX_SEL_SCALING_STAT_DISTRIBUTION }; + static constexpr DB2LoadInfo Instance{ Fields, 24, &ScalingStatDistributionMeta::Instance, HOTFIX_SEL_SCALING_STAT_DISTRIBUTION }; }; struct ScalingStatValuesLoadInfo diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 705b5252718..7cd81ae8799 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -220,6 +220,8 @@ DB2Storage<RandPropPointsEntry> sRandPropPointsStore("RandPropPo DB2Storage<RewardPackEntry> sRewardPackStore("RewardPack.db2", &RewardPackLoadInfo::Instance); DB2Storage<RewardPackXCurrencyTypeEntry> sRewardPackXCurrencyTypeStore("RewardPackXCurrencyType.db2", &RewardPackXCurrencyTypeLoadInfo::Instance); DB2Storage<RewardPackXItemEntry> sRewardPackXItemStore("RewardPackXItem.db2", &RewardPackXItemLoadInfo::Instance); +DB2Storage<ScalingStatDistributionEntry> sScalingStatDistributionStore("ScalingStatDistribution.db2", &ScalingStatDistributionLoadInfo::Instance); +DB2Storage<ScalingStatValuesEntry> sScalingStatValuesStore("ScalingStatValues.db2", &ScalingStatValuesLoadInfo::Instance); DB2Storage<ScenarioEntry> sScenarioStore("Scenario.db2", &ScenarioLoadInfo::Instance); DB2Storage<ScenarioStepEntry> sScenarioStepStore("ScenarioStep.db2", &ScenarioStepLoadInfo::Instance); DB2Storage<SceneScriptEntry> sSceneScriptStore("SceneScript.db2", &SceneScriptLoadInfo::Instance); @@ -417,6 +419,7 @@ namespace WMOAreaTableLookupContainer _wmoAreaTableLookup; std::array<std::unordered_map<int32, TalentTabEntry const*>, MAX_CLASSES> _talentTabsByIndex; std::unordered_map<uint32, std::vector<uint32>> _primaryTalentTreeSpellsByTalentTab; + std::unordered_map<uint32, ScalingStatValuesEntry const*> _scalingStatValuesByCharacterLevel; } void LoadDB2(std::bitset<TOTAL_LOCALES>& availableDb2Locales, std::vector<std::string>& errlist, StorageMap& stores, DB2StorageBase* storage, std::string const& db2Path, @@ -722,6 +725,8 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sRewardPackStore); LOAD_DB2(sRewardPackXCurrencyTypeStore); LOAD_DB2(sRewardPackXItemStore); + LOAD_DB2(sScalingStatDistributionStore); + LOAD_DB2(sScalingStatValuesStore); LOAD_DB2(sScenarioStore); LOAD_DB2(sScenarioStepStore); LOAD_DB2(sSceneScriptStore); @@ -1391,6 +1396,9 @@ void DB2Manager::IndexLoadedStores() sOldContinentsNodesMask[field] |= submask; } + for (ScalingStatValuesEntry const* ssd : sScalingStatValuesStore) + _scalingStatValuesByCharacterLevel[ssd->Charlevel] = ssd; + TC_LOG_INFO("server.loading", ">> Indexed DB2 data stores in {} ms", GetMSTimeDiffToNow(oldMSTime)); } @@ -2853,3 +2861,8 @@ std::vector<ItemEffectEntry const*> const* DB2Manager::GetItemEffectsForItemId(u { return Trinity::Containers::MapGetValuePtr(_itemEffectsByItemId, itemId); } + +ScalingStatValuesEntry const* DB2Manager::GetScalingStatValuesForLevel(uint32 level) const +{ + return Trinity::Containers::MapGetValuePtr(_scalingStatValuesByCharacterLevel, level); +} diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index bfe0ea997b9..34803198506 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -169,6 +169,8 @@ TC_GAME_API extern DB2Storage<QuestSortEntry> sQuestSortSt TC_GAME_API extern DB2Storage<QuestXPEntry> sQuestXPStore; TC_GAME_API extern DB2Storage<RandPropPointsEntry> sRandPropPointsStore; TC_GAME_API extern DB2Storage<RewardPackEntry> sRewardPackStore; +TC_GAME_API extern DB2Storage<ScalingStatDistributionEntry> sScalingStatDistributionStore; +TC_GAME_API extern DB2Storage<ScalingStatValuesEntry> sScalingStatValuesStore; TC_GAME_API extern DB2Storage<ScenarioEntry> sScenarioStore; TC_GAME_API extern DB2Storage<ScenarioStepEntry> sScenarioStepStore; TC_GAME_API extern DB2Storage<SkillLineEntry> sSkillLineStore; @@ -454,6 +456,7 @@ public: WMOAreaTableEntry const* GetWMOAreaTable(int32 rootId, int32 adtId, int32 groupId) const; std::unordered_set<uint32> const* GetPVPStatIDsForMap(uint32 mapId) const; std::vector<ItemEffectEntry const*> const* GetItemEffectsForItemId(uint32 itemId) const; + ScalingStatValuesEntry const* GetScalingStatValuesForLevel(uint32 characterLevel) const; private: friend class DB2HotfixGeneratorBase; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 0b72240c29c..836163b7f3b 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -2888,9 +2888,11 @@ struct RewardPackXItemEntry struct ScalingStatDistributionEntry { uint32 ID; - std::array<int32, 10> StatID; - std::array<int32, 10> Bonus; + uint16 PlayerLevelToItemLevelCurveID; + int32 Minlevel; int32 Maxlevel; + std::array<int32, 10> Bonus; + std::array<int32, 10> StatID; }; // structure for ScalingStatValues.db2 diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 0891ac897c5..dae8436cbe3 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -2033,10 +2033,59 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon return std::min(std::max(itemLevel, uint32(MIN_ITEM_LEVEL)), uint32(MAX_ITEM_LEVEL)); } -float Item::GetItemStatValue(uint32 index, Player const* /*owner*/) const +float Item::GetItemStatValue(uint32 index, Player const* /*owner*/, ScalingStatDistributionEntry const* ssd /*= nullptr*/, ScalingStatValuesEntry const* ssv /*= nullptr*/) const { ASSERT(index < MAX_ITEM_PROTO_STATS); + // Stats from Heirlooms + if (ssd && ssv) + { + if (ssd->StatID[index] == -1) + return 0.0f; + + int32 budget = 0; + switch (GetTemplate()->GetInventoryType()) + { + case INVTYPE_HEAD: + case INVTYPE_CHEST: + case INVTYPE_LEGS: + case INVTYPE_2HWEAPON: + case INVTYPE_ROBE: + budget = ssv->BudgetPrimary; + break; + case INVTYPE_SHOULDERS: + case INVTYPE_WAIST: + case INVTYPE_FEET: + case INVTYPE_HANDS: + case INVTYPE_TRINKET: + budget = ssv->BudgetSecondary; + break; + case INVTYPE_NECK: + case INVTYPE_WRISTS: + case INVTYPE_FINGER: + case INVTYPE_SHIELD: + case INVTYPE_CLOAK: + case INVTYPE_HOLDABLE: + budget = ssv->BudgetTertiary; + break; + case INVTYPE_RANGED: + case INVTYPE_THROWN: + case INVTYPE_RANGEDRIGHT: + case INVTYPE_RELIC: + budget = ssv->BudgetSub; + break; + case INVTYPE_WEAPON: + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_WEAPONOFFHAND: + budget = ssv->BudgetTrivial; + break; + default: + break; + } + + return static_cast<float>(budget * ssd->Bonus[index] / 10000.0f); + } + return static_cast<float>(_bonusData.ItemStatAmount[index]); } diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 9066b46ba45..7b3f18c4dff 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -287,7 +287,7 @@ class TC_GAME_API Item : public Object uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus); int32 GetRequiredLevel() const; int32 GetItemStatType(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_STATS); return _bonusData.ItemStatType[index]; } - float GetItemStatValue(uint32 index, Player const* owner) const; + float GetItemStatValue(uint32 index, Player const* owner, ScalingStatDistributionEntry const* ssd = nullptr, ScalingStatValuesEntry const* ssv = nullptr) const; SocketColor GetSocketColor(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_SOCKETS); return SocketColor(_bonusData.SocketColor[index]); } uint32 GetAppearanceModId() const { return m_itemData->ItemAppearanceModID; } void SetAppearanceModId(uint32 appearanceModId) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemAppearanceModID), appearanceModId); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 1d596c017c7..8dc39cdd221 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7393,7 +7393,7 @@ void Player::DuelComplete(DuelCompleteType type) //---------------------------------------------------------// -void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemAuras /*= true*/) +void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemAuras /*= true*/, bool updateHeirloomStats /*= false*/) { if (slot >= INVENTORY_SLOT_BAG_END || !item) return; @@ -7411,7 +7411,7 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemA if (item->GetSocketColor(0)) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items CorrectMetaGemEnchants(slot, apply); - _ApplyItemBonuses(item, slot, apply); + _ApplyItemBonuses(item, slot, apply, updateHeirloomStats); ApplyItemEquipSpell(item, apply); if (updateItemAuras) { @@ -7427,64 +7427,156 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemA TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: completed"); } -void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply) +void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply, bool onlyLevelScaling /*= false*/) { ItemTemplate const* proto = item->GetTemplate(); if (slot >= INVENTORY_SLOT_BAG_END || !proto) return; + ScalingStatDistributionEntry const* ssd = sScalingStatDistributionStore.LookupEntry(proto->GetScalingStatDistributionID()); + ScalingStatValuesEntry const* ssv = ssd ? sDB2Manager.GetScalingStatValuesForLevel(std::clamp<uint32>(GetLevel(), ssd->Minlevel, ssd->Maxlevel)) : nullptr; + if (onlyLevelScaling && (!ssd || !ssv)) + return; + for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) { int32 statType = item->GetItemStatType(i); - if (statType == -1) + if ((statType == -1 && !ssd) || (statType == -1 && ssd && ssd->StatID[i] == -1)) continue; - float val = item->GetItemStatValue(i, this); + if (statType == -1 && ssd) + statType = ssd->StatID[i]; + + float val = item->GetItemStatValue(i, this, ssd, ssv); if (val == 0) continue; ApplyItemModModifier(static_cast<ItemModType>(statType), val, apply); } + // Spellpower bonus from Heirlooms + if (ssd && proto->HasFlag(ItemFlags2::ITEM_FLAG2_CASTER_WEAPON)) + if (int32 spellPowerBonus = ssv->SpellPower) + ApplySpellPowerBonus(spellPowerBonus, apply); + + // Armor bonus from Heirlooms + int32 overrideArmor = 0; + if (ssd && proto->GetClass() == ITEM_CLASS_ARMOR) + { + switch (proto->GetInventoryType()) + { + case INVTYPE_HEAD: + overrideArmor = ssv->HeadArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_SHOULDERS: + overrideArmor = ssv->ShoulderArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_CHEST: + case INVTYPE_ROBE: + overrideArmor = ssv->ChestArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_LEGS: + overrideArmor = ssv->LegsArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_FEET: + overrideArmor = ssv->FeetArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_WAIST: + overrideArmor = ssv->WaistArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_HANDS: + overrideArmor = ssv->HandsArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_WRISTS: + overrideArmor = ssv->WristsArmor[proto->GetSubClass() - 1]; + break; + case INVTYPE_CLOAK: + overrideArmor = ssv->ClothCloakArmor; + break; + default: + break; + } + } + for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) - if (int16 resistance = proto->GetResistance(SpellSchools(i))) + { + int16 resistance = proto->GetResistance(SpellSchools(i)); + if (i == SPELL_SCHOOL_NORMAL && overrideArmor) + resistance = overrideArmor; + + if (resistance) + { + if (i == SPELL_SCHOOL_NORMAL && overrideArmor) + resistance = overrideArmor; + HandleStatFlatModifier(UnitMods(UNIT_MOD_ARMOR + i), BASE_VALUE, float(resistance), apply); + } + } WeaponAttackType attType = Player::GetAttackBySlot(slot, proto->GetInventoryType()); if (attType != MAX_ATTACK) - _ApplyWeaponDamage(slot, item, apply); + _ApplyWeaponDamage(slot, item, ssv, apply); } // @TODO: Ovahlord ScalingStat db2s -void Player::_ApplyWeaponDamage(uint8 slot, Item* item, bool apply) +void Player::_ApplyWeaponDamage(uint8 slot, Item* item, ScalingStatValuesEntry const* ssv, bool apply) { ItemTemplate const* proto = item->GetTemplate(); WeaponAttackType attType = Player::GetAttackBySlot(slot, proto->GetInventoryType()); if (!IsInFeralForm() && apply && !CanUseAttackType(attType)) return; - // ScalingStatDistributionEntry const* ssd = sScalingStatDistributionStore.LookupEntry(proto->GetScalingStatDistributionID()); - // ScalingStatValuesEntry const* ssv = (ssd && proto->GetScalingStatValue() != 0) ? sDB2Manager.GetScalingStatValuesForLevel(std::clamp<uint32>(GetLevel(), ssd->MinLevel, ssd->MaxLevel)) : nullptr; - float damage = 0.0f; //for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) { int32 minDamage = proto->GetMinDamage(0); int32 maxDamage = proto->GetMaxDamage(0); - // If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage - // if (ssv) - // { - // int32 extraDPS = ssv->getDPSMod(proto->GetScalingStatValue()); - // if (extraDPS) - // { - // float average = extraDPS * proto->GetDelay() / 1000.0f; - // float mod = ssv->isTwoHand(proto->GetScalingStatValue()) ? 0.2f : 0.3f; - // - // minDamage = (1.0f - mod) * average; - // maxDamage = (1.0f + mod) * average; - // } - // } + // Weapon damage from Heirlooms + if (ssv) + { + float multiplier = 0.0f; + int32 dps = 0; + switch (proto->GetSubClass()) + { + case ITEM_SUBCLASS_WEAPON_AXE2: + case ITEM_SUBCLASS_WEAPON_MACE2: + case ITEM_SUBCLASS_WEAPON_POLEARM: + case ITEM_SUBCLASS_WEAPON_SWORD2: + case ITEM_SUBCLASS_WEAPON_STAFF: + case ITEM_SUBCLASS_WEAPON_FISHING_POLE: + dps = proto->HasFlag(ItemFlags2::ITEM_FLAG2_CASTER_WEAPON) ? ssv->SpellcasterDPS2H : ssv->WeaponDPS2H; + multiplier = 0.2f; + break; + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_GUN: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + dps = ssv->RangedDPS; + multiplier = 0.3f; + break; + case ITEM_SUBCLASS_WEAPON_AXE: + case ITEM_SUBCLASS_WEAPON_MACE: + case ITEM_SUBCLASS_WEAPON_SWORD: + case ITEM_SUBCLASS_WEAPON_DAGGER: + case ITEM_SUBCLASS_WEAPON_THROWN: + dps = proto->HasFlag(ItemFlags2::ITEM_FLAG2_CASTER_WEAPON) ? ssv->SpellcasterDPS1H : ssv->WeaponDPS1H; + multiplier = 0.3f; + break; + case ITEM_SUBCLASS_WEAPON_WAND: + dps = ssv->WandDPS; + multiplier = 0.3f; + break; + default: + break; + } + + if (dps > 0) + { + float average = dps * proto->GetDelay() / 1000.0f; + minDamage = (1.0f - multiplier) * average; + maxDamage = (1.0f + multiplier) * average; + } + } if (minDamage > 0) { @@ -8109,7 +8201,7 @@ void Player::_ApplyAllLevelScaleItemMods(bool apply) if (!CanUseAttackType(Player::GetAttackBySlot(i, m_items[i]->GetTemplate()->GetInventoryType()))) continue; - _ApplyItemMods(m_items[i], i, apply); + _ApplyItemMods(m_items[i], i, apply, true); // Update item sets for heirlooms if (sDB2Manager.GetHeirloomByItemId(m_items[i]->GetEntry()) && m_items[i]->GetTemplate()->GetItemSet()) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b5033940eba..698d8eff36c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -54,6 +54,7 @@ struct Mail; struct MapEntry; struct QuestPackageItemEntry; struct RewardPackEntry; +struct ScalingStatValuesEntry; struct SkillRaceClassInfoEntry; struct SpellCastRequest; struct TalentEntry; @@ -2305,12 +2306,12 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect const* aurEff) const override; - void _ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemAuras = true); + void _ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemAuras = true, bool updateHeirloomStats = false); void _RemoveAllItemMods(); void _ApplyAllItemMods(); void _ApplyAllLevelScaleItemMods(bool apply); - void _ApplyItemBonuses(Item* item, uint8 slot, bool apply); - void _ApplyWeaponDamage(uint8 slot, Item* item, bool apply); + void _ApplyItemBonuses(Item* item, uint8 slot, bool apply, bool onlyLevelScaling = false); + void _ApplyWeaponDamage(uint8 slot, Item* item, ScalingStatValuesEntry const* ssv, bool apply); bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) const; void ToggleMetaGemsActive(uint8 exceptslot, bool apply); void CorrectMetaGemEnchants(uint8 slot, bool apply); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 0e636e14218..76f38ad7632 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2021,7 +2021,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (!target->CanUseAttackType(BASE_ATTACK)) { if (Item* pItem = target->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) - target->ToPlayer()->_ApplyWeaponDamage(EQUIPMENT_SLOT_MAINHAND, pItem, apply); + target->ToPlayer()->_ApplyWeaponDamage(EQUIPMENT_SLOT_MAINHAND, pItem, nullptr, apply); } } @@ -2461,7 +2461,7 @@ void AuraEffect::HandleAuraModDisarm(AuraApplication const* aurApp, uint8 mode, player->ApplyItemDependentAuras(item, !apply); if (attackType != MAX_ATTACK) { - player->_ApplyWeaponDamage(slot, item, !apply); + player->_ApplyWeaponDamage(slot, item, nullptr, !apply); if (!apply) // apply case already handled on item dependent aura removal (if any) player->UpdateWeaponDependentAuras(attackType); } |