diff options
author | Ovahlord <dreadkiller@gmx.de> | 2023-11-28 02:00:51 +0100 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2023-11-28 02:00:51 +0100 |
commit | 00c1f57c8bcd22bbf202f921d9670b1af9c3b3fd (patch) | |
tree | 05e305cea0fb7a8f0075056f69e32a4e542f4153 /src | |
parent | bbb320f39013bb540d69a36fe7be72c1ac6ba9d3 (diff) |
Core/Items: restore item random properties handling
Diffstat (limited to 'src')
23 files changed, 304 insertions, 145 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index b4a5d2793cd..358faecb9f5 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -24,7 +24,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() m_stmts.resize(MAX_CHARACTERDATABASE_STATEMENTS); #define SelectItemInstanceContent "ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, " \ - "ii.durability, ii.playedTime, ii.text, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.context, " \ + "ii.durability, ii.playedTime, ii.text, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.randomPropertiesId, ii.randomPropertiesSeed, ii.context, " \ "iit.itemModifiedAppearanceAllSpecs, iit.itemModifiedAppearanceSpec1, iit.itemModifiedAppearanceSpec2, iit.itemModifiedAppearanceSpec3, iit.itemModifiedAppearanceSpec4, iit.itemModifiedAppearanceSpec5, " \ "iit.spellItemEnchantmentAllSpecs, iit.spellItemEnchantmentSpec1, iit.spellItemEnchantmentSpec2, iit.spellItemEnchantmentSpec3, iit.spellItemEnchantmentSpec4, iit.spellItemEnchantmentSpec5, " \ "iit.secondaryItemModifiedAppearanceAllSpecs, iit.secondaryItemModifiedAppearanceSpec1, iit.secondaryItemModifiedAppearanceSpec2, iit.secondaryItemModifiedAppearanceSpec3, iit.secondaryItemModifiedAppearanceSpec4, iit.itemModifiedAppearanceSpec5, " \ @@ -190,8 +190,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_ITEM_BOP_TRADE, "DELETE FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ITEM_BOP_TRADE, "INSERT INTO item_soulbound_trade_data VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_INVENTORY_ITEM, "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, durability, playedTime, text, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, durability = ?, playedTime = ?, text = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, context = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, durability, playedTime, text, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, randomPropertiesId, randomPropertiesSeed, context, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, durability = ?, playedTime = ?, text = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, randomPropertiesId = ?, randomPropertiesSeed = ?, context = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC); @@ -625,8 +625,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_TRAIT_CONFIGS_BY_CHAR, "DELETE FROM character_trait_config WHERE guid = ?", CONNECTION_ASYNC); // Void Storage - PrepareStatement(CHAR_SEL_CHAR_VOID_STORAGE, "SELECT itemId, itemEntry, slot, creatorGuid, fixedScalingLevel, context FROM character_void_storage WHERE playerGuid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_REP_CHAR_VOID_STORAGE_ITEM, "REPLACE INTO character_void_storage (itemId, playerGuid, itemEntry, slot, creatorGuid, fixedScalingLevel, context) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_VOID_STORAGE, "SELECT itemId, itemEntry, slot, creatorGuid, fixedScalingLevel, randomPropertiesId, randomPropertiesSeed, context FROM character_void_storage WHERE playerGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_CHAR_VOID_STORAGE_ITEM, "REPLACE INTO character_void_storage (itemId, playerGuid, itemEntry, slot, creatorGuid, fixedScalingLevel, randomPropertiesId, randomPropertiesSeed, context) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_VOID_STORAGE_ITEM_BY_CHAR_GUID, "DELETE FROM character_void_storage WHERE playerGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_VOID_STORAGE_ITEM_BY_SLOT, "DELETE FROM character_void_storage WHERE slot = ? AND playerGuid = ?", CONNECTION_ASYNC); @@ -637,10 +637,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_CUF_PROFILES, "DELETE FROM character_cuf_profiles WHERE guid = ?", CONNECTION_ASYNC); // Items that hold loot or money - PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, context FROM item_loot_items", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, random_properties_id, random_properties_seed, context FROM item_loot_items", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_ITEMCONTAINER_ITEMS, "DELETE FROM item_loot_items WHERE container_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEMCONTAINER_ITEM, "DELETE FROM item_loot_items WHERE container_id = ? AND item_id = ? AND item_count = ? AND item_index = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_ITEMCONTAINER_ITEMS, "INSERT INTO item_loot_items (container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, context) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ITEMCONTAINER_ITEMS, "INSERT INTO item_loot_items (container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, random_properties_id, random_properties_seed, context) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_ITEMCONTAINER_MONEY, "SELECT container_id, money FROM item_loot_money", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_ITEMCONTAINER_MONEY, "DELETE FROM item_loot_money WHERE container_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ITEMCONTAINER_MONEY, "INSERT INTO item_loot_money (container_id, money) VALUES (?, ?)", CONNECTION_ASYNC); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 7e72ffdb8d7..0d149078279 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -525,12 +525,12 @@ void AuctionHouseMgr::LoadAuctions() } Item* item = NewItemOrBag(proto); - if (!item->LoadFromDB(itemGuid, ObjectGuid::Create<HighGuid::Player>(fields[44].GetUInt64()), fields, itemEntry)) + if (!item->LoadFromDB(itemGuid, ObjectGuid::Create<HighGuid::Player>(fields[46].GetUInt64()), fields, itemEntry)) { delete item; continue; } - uint32 auctionId = fields[45].GetUInt32(); + uint32 auctionId = fields[47].GetUInt32(); itemsByAuction[auctionId].push_back(item); ++count; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 6b50a5cfc07..bae0ec127b6 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -770,6 +770,8 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sItemModifiedAppearanceExtraStore); LOAD_DB2(sItemNameDescriptionStore); LOAD_DB2(sItemPriceBaseStore); + LOAD_DB2(sItemRandomPropertiesStore); + LOAD_DB2(sItemRandomSuffixStore); LOAD_DB2(sItemSearchNameStore); LOAD_DB2(sItemSetStore); LOAD_DB2(sItemSetSpellStore); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index be53397f52e..700c841b398 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -431,6 +431,8 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans) stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID)); + stmt->setInt32(++index, m_itemData->RandomPropertiesID); + stmt->setInt32(++index, m_itemData->PropertySeed); stmt->setUInt8(++index, uint8(m_itemData->Context)); stmt->setUInt64(++index, GetGUID().GetCounter()); @@ -662,47 +664,48 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[14].GetUInt16()); SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[15].GetUInt32()); - SetContext(ItemContext(fields[16].GetUInt8())); + SetItemRandomProperties(ItemRandomProperties(fields[16].GetInt32(), fields[17].GetInt32())); + SetContext(ItemContext(fields[18].GetUInt8())); // load charges after bonuses, they can add more item effects std::vector<std::string_view> tokens = Trinity::Tokenize(fields[6].GetStringView(), ' ', false); for (uint8 i = 0; i < m_itemData->SpellCharges.size() && i < _bonusData.EffectCount && i < tokens.size(); ++i) SetSpellCharges(i, Trinity::StringTo<int32>(tokens[i]).value_or(0)); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[17].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[18].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[19].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[20].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[21].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_5, fields[22].GetUInt32()); - - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[23].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[24].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[25].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[26].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[27].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_5, fields[28].GetUInt32()); - - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, fields[29].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1, fields[30].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2, fields[31].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3, fields[32].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4, fields[33].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_5, fields[34].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[19].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[20].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[21].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[22].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[23].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_5, fields[24].GetUInt32()); + + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[25].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[26].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[27].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[28].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[29].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_5, fields[30].GetUInt32()); + + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, fields[31].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1, fields[32].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2, fields[33].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3, fields[34].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4, fields[35].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_5, fields[36].GetUInt32()); uint32 const gemFields = 3; ItemDynamicFieldGems gemData[MAX_GEM_SOCKETS]; memset(gemData, 0, sizeof(gemData)); for (uint32 i = 0; i < MAX_GEM_SOCKETS; ++i) { - gemData[i].ItemId = fields[35 + i * gemFields].GetUInt32(); - std::vector<std::string_view> gemBonusListIDs = Trinity::Tokenize(fields[36 + i * gemFields].GetStringView(), ' ', false); + gemData[i].ItemId = fields[37 + i * gemFields].GetUInt32(); + std::vector<std::string_view> gemBonusListIDs = Trinity::Tokenize(fields[38 + i * gemFields].GetStringView(), ' ', false); uint32 b = 0; for (std::string_view token : gemBonusListIDs) if (Optional<uint16> bonusListID = Trinity::StringTo<uint16>(token)) gemData[i].BonusListIDs[b++] = *bonusListID; - gemData[i].Context = fields[37 + i * gemFields].GetUInt8(); + gemData[i].Context = fields[39 + i * gemFields].GetUInt8(); if (gemData[i].ItemId) SetGem(i, &gemData[i]); } @@ -800,6 +803,43 @@ uint32 Item::GetSkill() return proto->GetSkill(); } +void Item::SetItemRandomProperties(ItemRandomProperties randomProperties) +{ + if (!randomProperties.RandomPropertiesID) + return; + + if (randomProperties.RandomPropertiesID > 0) + { + ItemRandomPropertiesEntry const* randomPropertiesEntry = sItemRandomPropertiesStore.LookupEntry(randomProperties.RandomPropertiesID); + if (randomPropertiesEntry) + { + if (m_itemData->RandomPropertiesID != randomProperties.RandomPropertiesID) + { + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::RandomPropertiesID), randomProperties.RandomPropertiesID); + SetState(ITEM_CHANGED, GetOwner()); + } + for (uint32 i = PROP_ENCHANTMENT_SLOT_2; i < PROP_ENCHANTMENT_SLOT_2 + 3; ++i) + SetEnchantment(EnchantmentSlot(i), randomPropertiesEntry->Enchantment[i - PROP_ENCHANTMENT_SLOT_2], 0, 0); + } + } + else + { + ItemRandomSuffixEntry const* randomSuffixEntry = sItemRandomSuffixStore.LookupEntry(std::abs(randomProperties.RandomPropertiesID)); + if (randomSuffixEntry) + { + if (m_itemData->RandomPropertiesID != randomProperties.RandomPropertiesID || !m_itemData->PropertySeed) + { + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::RandomPropertiesID), randomProperties.RandomPropertiesID); + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::PropertySeed), randomProperties.RandomPropertiesSeed); + SetState(ITEM_CHANGED, GetOwner()); + } + + for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i) + SetEnchantment(EnchantmentSlot(i), randomSuffixEntry->Enchantment[i - PROP_ENCHANTMENT_SLOT_0], 0, 0); + } + } +} + void Item::SetState(ItemUpdateState state, Player* forplayer) { if (uState == ITEM_NEW && state == ITEM_REMOVED) diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 3ddfc5a2c78..5b45452be49 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -221,6 +221,8 @@ class TC_GAME_API Item : public Object uint32 GetSkill(); + void SetItemRandomProperties(ItemRandomProperties randomProperties); + void SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges, ObjectGuid caster = ObjectGuid::Empty); void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration, Player* owner); void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges); diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp index e13ea59471b..3634f5c8ed4 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp @@ -26,64 +26,81 @@ #include <list> #include <vector> -namespace +/*static*/ ItemEnchantmentMgr* ItemEnchantmentMgr::instance() { - struct RandomBonusListIds - { - std::vector<int32> BonusListIDs; - std::vector<double> Chances; - }; - - std::unordered_map<uint32, RandomBonusListIds> _storage; + static ItemEnchantmentMgr instance; + return &instance; } -ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id) +void ItemEnchantmentMgr::LoadRandomEnchantmentsTable() { - ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_id); - if (!itemProto) - return 0; + uint32 oldMSTime = getMSTime(); - // item must have one from this field values not null if it can have random enchantments - if (!itemProto->RandomBonusListTemplateId) - return 0; + _storage.clear(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT Id, EnchantmentId, Chance FROM item_random_enchantment_template"); - auto tab = _storage.find(itemProto->RandomBonusListTemplateId); - if (tab == _storage.end()) + if (result) { - TC_LOG_ERROR("sql.sql", "Item RandomBonusListTemplateId id #{} used in `item_template_addon` but it does not have records in `item_random_bonus_list_template` table.", itemProto->RandomBonusListTemplateId); - return 0; - } + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint32 enchantmentId = fields[1].GetUInt32(); + float chance = fields[2].GetFloat(); + + if (!sItemRandomPropertiesStore.LookupEntry(enchantmentId) && !sItemRandomSuffixStore.LookupEntry(enchantmentId)) + { + TC_LOG_ERROR("sql.sql", "ItemRandomProperties/ItemRandomSuffix Id {} used in `item_random_enchantment_template` by id {} doesn't have exist in its corresponding db2 file.", enchantmentId, id); + continue; + } + + if (chance < 0.000001f || chance > 100.0f) + { + TC_LOG_ERROR("sql.sql", "Enchantment Id {} used in `item_random_enchantment_template` by id {} has invalid chance {}", enchantmentId, id, chance); + continue; + } + + RandomEnchantmentData& ids = _storage[id]; + ids.EnchantmentIDs.push_back(enchantmentId); + ids.Chances.push_back(chance); - return *Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.BonusListIDs, std::span(tab->second.Chances)); + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded {} Random item enchantment definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + else + TC_LOG_INFO("server.loading", ">> Loaded 0 Random item bonus list definitions. DB table `item_random_enchantment_template` is empty."); } -TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subClass) +static int32 GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType) { - uint32 propIndex; + uint32 propIndex = 0; switch (inventoryType) { + // Items of that type don`t have points + case INVTYPE_NON_EQUIP: + case INVTYPE_BAG: + case INVTYPE_TABARD: + case INVTYPE_AMMO: + case INVTYPE_QUIVER: + case INVTYPE_RELIC: + return 0; + // Select point coefficient case INVTYPE_HEAD: case INVTYPE_BODY: case INVTYPE_CHEST: case INVTYPE_LEGS: - case INVTYPE_RANGED: case INVTYPE_2HWEAPON: case INVTYPE_ROBE: - case INVTYPE_THROWN: propIndex = 0; break; - case INVTYPE_RANGEDRIGHT: - if (subClass == ITEM_SUBCLASS_WEAPON_WAND) - propIndex = 3; - else - propIndex = 0; - break; - case INVTYPE_WEAPON: - case INVTYPE_WEAPONMAINHAND: - case INVTYPE_WEAPONOFFHAND: - propIndex = 3; - break; case INVTYPE_SHOULDERS: case INVTYPE_WAIST: case INVTYPE_FEET: @@ -99,7 +116,14 @@ TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint case INVTYPE_HOLDABLE: propIndex = 2; break; - case INVTYPE_RELIC: + case INVTYPE_WEAPON: + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_WEAPONOFFHAND: + propIndex = 3; + break; + case INVTYPE_RANGED: + case INVTYPE_THROWN: + case INVTYPE_RANGEDRIGHT: propIndex = 4; break; default: @@ -125,3 +149,56 @@ TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint return 0; } + +ItemRandomProperties ItemEnchantmentMgr::GenerateRandomProperties(uint32 itemId) +{ + ItemRandomProperties properties; + ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemId); + if (!itemProto) + return properties; + + if (!itemProto->GetRandomSelect() && !itemProto->GetRandomSuffixGroupID()) + return properties; + + if (uint16 randomSelect = itemProto->GetRandomSelect()) + { + auto tab = _storage.find(randomSelect); + if (tab == _storage.end()) + { + TC_LOG_ERROR("sql.sql", "Item RandomSelect Id {} used but it does not have records in `item_random_enchantment_template` table.", randomSelect); + return properties; + } + + uint32 randomPropertiesId = *Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.EnchantmentIDs, std::span(tab->second.Chances)); + ItemRandomPropertiesEntry const* randomPropertiesEntry = sItemRandomPropertiesStore.LookupEntry(randomPropertiesId); + if (!randomPropertiesEntry) + { + TC_LOG_ERROR("sql.sql", "Enchantment Id {} used but it doesn't have records in 'ItemRandomProperties.db2'", randomPropertiesId); + return properties; + } + + properties.RandomPropertiesID = static_cast<int32>(randomPropertiesEntry->ID); + } + else if (uint16 randomSuffix = itemProto->GetRandomSuffixGroupID()) + { + auto tab = _storage.find(randomSuffix); + if (tab == _storage.end()) + { + TC_LOG_ERROR("sql.sql", "Item RandomSuffixGroupID Id {} used but it does not have records in `item_random_enchantment_template` table.", randomSuffix); + return properties; + } + + uint32 randomSuffixId = *Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.EnchantmentIDs, std::span(tab->second.Chances)); + ItemRandomSuffixEntry const* randomSuffixEntry = sItemRandomSuffixStore.LookupEntry(randomSuffixId); + if (!randomSuffixEntry) + { + TC_LOG_ERROR("sql.sql", "Enchantment id Id {} used but it doesn't have records in 'ItemRandomSuffixEntry.db2'", randomSuffixId); + return properties; + } + + properties.RandomPropertiesID = -static_cast<int32>(randomSuffixEntry->ID); + properties.RandomPropertiesSeed = GetRandomPropertyPoints(itemProto->GetItemLevel(), itemProto->GetQuality(), itemProto->GetInventoryType()); + } + + return properties; +} diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.h b/src/server/game/Entities/Item/ItemEnchantmentMgr.h index 05000adf6e2..6bfc43ab36f 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.h +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.h @@ -20,9 +20,34 @@ #include "Common.h" -using ItemRandomBonusListId = uint32; +struct ItemRandomProperties +{ + ItemRandomProperties(int32 randomPropertiesId = 0, int32 randomPropertiesSeed = 0) : RandomPropertiesID(randomPropertiesId), RandomPropertiesSeed(randomPropertiesSeed) { } -TC_GAME_API ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id); -TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subclass); + int32 RandomPropertiesID = 0; + int32 RandomPropertiesSeed = 0; +}; + + +struct RandomEnchantmentData +{ + std::vector<uint16> EnchantmentIDs; + std::vector<double> Chances; +}; + + +class TC_GAME_API ItemEnchantmentMgr +{ +public: + static ItemEnchantmentMgr* instance(); + + void LoadRandomEnchantmentsTable(); + ItemRandomProperties GenerateRandomProperties(uint32 itemId); + +private: + std::unordered_map<uint32, RandomEnchantmentData> _storage; +}; + +#define sItemEnchantmentMgr ItemEnchantmentMgr::instance() #endif diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 6f824208833..29eadab8fe7 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -811,6 +811,8 @@ struct TC_GAME_API ItemTemplate int32 GetScalingStatValue() const { return BasicData->ScalingStatValue; } uint16 GetMinDamage(uint8 index) const { return BasicData->MinDamage[index]; } uint16 GetMaxDamage(uint8 index) const { return BasicData->MaxDamage[index]; } + uint16 GetRandomSuffixGroupID() const { return BasicData->ItemRandomSuffixGroupID; } + uint16 GetRandomSelect() const { return BasicData->RandomSelect; } uint32 MaxDurability; std::vector<ItemEffectEntry const*> Effects; @@ -822,7 +824,6 @@ struct TC_GAME_API ItemTemplate uint32 MaxMoneyLoot; uint32 FlagsCu; float SpellPPMRate; - uint32 RandomBonusListTemplateId; std::bitset<MAX_CLASSES * MAX_SPECIALIZATIONS> Specializations[3]; // one set for 1-40 level range and another for 41-109 and one for 110 uint32 ItemSpecClassMask; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3d5ba089e49..a93c6444d3b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -588,7 +588,7 @@ bool Player::StoreNewItemInBestSlots(uint32 itemId, uint32 amount, ItemContext c InventoryResult msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, itemId, amount); if (msg == EQUIP_ERR_OK) { - StoreNewItem(sDest, itemId, true, GuidSet(), context); + StoreNewItem(sDest, itemId, true, sItemEnchantmentMgr->GenerateRandomProperties(itemId), GuidSet(), context); return true; // stored } @@ -3934,7 +3934,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe do { Field* fields = resultItems->Fetch(); - uint64 mailId = fields[44].GetUInt64(); + uint64 mailId = fields[46].GetUInt64(); if (Item* mailItem = _LoadMailedItem(playerguid, nullptr, mailId, nullptr, fields)) itemsByMail[mailId].push_back(mailItem); @@ -11199,7 +11199,7 @@ InventoryResult Player::CanRollNeedForItem(ItemTemplate const* proto, Map const* } // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case. -Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, GuidSet const& allowedLooters /*= GuidSet()*/, ItemContext context /*= ItemContext::NONE*/, bool addToCollection /*= true*/) +Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, ItemRandomProperties randomProperties /*= ItemRandomProperties()*/, GuidSet const& allowedLooters /*= GuidSet()*/, ItemContext context /*= ItemContext::NONE*/, bool addToCollection /*= true*/) { uint32 count = 0; for (ItemPosCountVec::const_iterator itr = pos.begin(); itr != pos.end(); ++itr) @@ -11216,6 +11216,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat UpdateCriteria(CriteriaType::AcquireItem, itemId, count); item->SetFixedLevel(GetLevel()); + item->SetItemRandomProperties(randomProperties); if (allowedLooters.size() > 1 && item->GetTemplate()->GetMaxStackSize() == 1 && item->IsSoulBound()) { @@ -13167,50 +13168,42 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } break; case ITEM_ENCHANTMENT_TYPE_RESISTANCE: - if (pEnchant->ScalingClass) + if (!enchant_amount) { - int32 scalingClass = pEnchant->ScalingClass; - if ((*m_unitData->MinItemLevel || *m_unitData->MaxItemLevel) && pEnchant->ScalingClassRestricted) - scalingClass = pEnchant->ScalingClassRestricted; - - uint8 minLevel = pEnchant->GetFlags().HasFlag(SpellItemEnchantmentFlags::ScaleAsAGem) ? 1 : 60; - uint8 scalingLevel = GetLevel(); - uint8 maxLevel = uint8(pEnchant->MaxLevel ? pEnchant->MaxLevel : sSpellScalingGameTable.GetTableRowCount() - 1); - - if (minLevel > GetLevel()) - scalingLevel = minLevel; - else if (maxLevel < GetLevel()) - scalingLevel = maxLevel; - - if (GtSpellScalingEntry const* spellScaling = sSpellScalingGameTable.GetRow(scalingLevel)) - enchant_amount = uint32(pEnchant->EffectScalingPoints[s] * GetSpellScalingColumnForClass(spellScaling, scalingClass)); + ItemRandomSuffixEntry const* randomSuffixEntries = sItemRandomSuffixStore.LookupEntry(std::abs(item->m_itemData->RandomPropertiesID)); + if (randomSuffixEntries) + { + for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) + { + if (randomSuffixEntries->Enchantment[k] == enchant_id) + { + enchant_amount = uint32((static_cast<double>(randomSuffixEntries->AllocationPct[k]) * item->m_itemData->PropertySeed) / 10000); + break; + } + } + } } - enchant_amount = std::max(enchant_amount, 1u); + HandleStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply); break; case ITEM_ENCHANTMENT_TYPE_STAT: { - if (pEnchant->ScalingClass) + if (!enchant_amount) { - int32 scalingClass = pEnchant->ScalingClass; - if ((*m_unitData->MinItemLevel || *m_unitData->MaxItemLevel) && pEnchant->ScalingClassRestricted) - scalingClass = pEnchant->ScalingClassRestricted; - - uint8 minLevel = pEnchant->GetFlags().HasFlag(SpellItemEnchantmentFlags::ScaleAsAGem) ? 1 : 60; - uint8 scalingLevel = GetLevel(); - uint8 maxLevel = uint8(pEnchant->MaxLevel ? pEnchant->MaxLevel : sSpellScalingGameTable.GetTableRowCount() - 1); - - if (minLevel > GetLevel()) - scalingLevel = minLevel; - else if (maxLevel < GetLevel()) - scalingLevel = maxLevel; - - if (GtSpellScalingEntry const* spellScaling = sSpellScalingGameTable.GetRow(scalingLevel)) - enchant_amount = uint32(pEnchant->EffectScalingPoints[s] * GetSpellScalingColumnForClass(spellScaling, scalingClass)); + ItemRandomSuffixEntry const* randomSuffixEntry = sItemRandomSuffixStore.LookupEntry(std::abs(item->m_itemData->RandomPropertiesID)); + if (randomSuffixEntry) + { + for (uint8 k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) + { + if (randomSuffixEntry->Enchantment[k] == enchant_id) + { + enchant_amount = uint32((static_cast<double>(randomSuffixEntry->AllocationPct[k]) * item->m_itemData->PropertySeed) / 10000); + break; + } + } + } } - enchant_amount = std::max(enchant_amount, 1u); - TC_LOG_DEBUG("entities.player.items", "Adding {} to stat nb {}", enchant_amount, enchant_spell_id); switch (enchant_spell_id) { @@ -14603,7 +14596,7 @@ void Player::RewardQuestPackage(uint32 questPackageId, ItemContext context, uint ItemPosCountVec dest; if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemQuantity) == EQUIP_ERR_OK) { - Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, {}, context); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, sItemEnchantmentMgr->GenerateRandomProperties(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -14622,7 +14615,7 @@ void Player::RewardQuestPackage(uint32 questPackageId, ItemContext context, uint ItemPosCountVec dest; if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemQuantity) == EQUIP_ERR_OK) { - Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, {}, context); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, sItemEnchantmentMgr->GenerateRandomProperties(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -14682,7 +14675,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew ItemPosCountVec dest; if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardItemCount[i]) == EQUIP_ERR_OK) { - Item* item = StoreNewItem(dest, itemId, true, {}, ItemContext::Quest_Reward); + Item* item = StoreNewItem(dest, itemId, true, sItemEnchantmentMgr->GenerateRandomProperties(itemId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardItemCount[i], true, false); } else if (quest->IsDFQuest()) @@ -14727,7 +14720,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew ItemPosCountVec dest; if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, rewardId, quest->RewardChoiceItemCount[i]) == EQUIP_ERR_OK) { - Item* item = StoreNewItem(dest, rewardId, true, {}, ItemContext::Quest_Reward); + Item* item = StoreNewItem(dest, rewardId, true, sItemEnchantmentMgr->GenerateRandomProperties(rewardId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardChoiceItemCount[i], true, false); } } @@ -18062,8 +18055,8 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) Field* fields = result->Fetch(); if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields)) { - ObjectGuid bagGuid = fields[44].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[44].GetUInt64()) : ObjectGuid::Empty; - uint8 slot = fields[45].GetUInt8(); + ObjectGuid bagGuid = fields[46].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[44].GetUInt64()) : ObjectGuid::Empty; + uint8 slot = fields[47].GetUInt8(); GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item); GetSession()->GetCollectionMgr()->AddItemAppearance(item); @@ -18188,6 +18181,7 @@ void Player::_LoadVoidStorage(PreparedQueryResult result) uint8 slot = fields[2].GetUInt8(); ObjectGuid creatorGuid = fields[3].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[3].GetUInt64()) : ObjectGuid::Empty; uint32 fixedScalingLevel = fields[4].GetUInt32(); + ItemRandomProperties randomProperties = ItemRandomProperties(fields[5].GetInt32(), fields[6].GetInt32()); ItemContext context = ItemContext(fields[5].GetUInt8()); if (!itemId) { @@ -18210,7 +18204,7 @@ void Player::_LoadVoidStorage(PreparedQueryResult result) continue; } - _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, fixedScalingLevel, context); + _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, fixedScalingLevel, randomProperties, context); WorldPackets::Item::ItemInstance voidInstance; voidInstance.Initialize(_voidStorageItems[slot]); @@ -19852,7 +19846,9 @@ void Player::_SaveVoidStorage(CharacterDatabaseTransaction trans) stmt->setUInt8(3, i); stmt->setUInt64(4, _voidStorageItems[i]->CreatorGuid.GetCounter()); stmt->setUInt32(5, _voidStorageItems[i]->FixedScalingLevel); - stmt->setUInt8(6, AsUnderlyingType(_voidStorageItems[i]->Context)); + stmt->setInt32(6, _voidStorageItems[i]->RandomProperties.RandomPropertiesID); + stmt->setInt32(7, _voidStorageItems[i]->RandomProperties.RandomPropertiesSeed); + stmt->setUInt8(8, AsUnderlyingType(_voidStorageItems[i]->Context)); } trans->Append(stmt); @@ -21987,7 +21983,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c } Item* it = bStore ? - StoreNewItem(vDest, item, true, {}, ItemContext::Vendor, false) : + StoreNewItem(vDest, item, true, sItemEnchantmentMgr->GenerateRandomProperties(item), {}, ItemContext::Vendor, false) : EquipNewItem(uiDest, item, ItemContext::Vendor, true); if (it) { @@ -25520,7 +25516,7 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count); if (msg == EQUIP_ERR_OK) { - Item* newitem = StoreNewItem(dest, item->itemid, true, item->GetAllowedLooters(), item->context); + Item* newitem = StoreNewItem(dest, item->itemid, true, sItemEnchantmentMgr->GenerateRandomProperties(item->itemid), item->GetAllowedLooters(), item->context); if (ffaItem) { @@ -27367,7 +27363,7 @@ bool Player::AddItem(uint32 itemId, uint32 count) return false; } - Item* item = StoreNewItem(dest, itemId, true); + Item* item = StoreNewItem(dest, itemId, true, sItemEnchantmentMgr->GenerateRandomProperties(itemId)); if (item) SendNewItem(item, count, true, false); else diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 976e52fbc93..b4a32eba905 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1006,15 +1006,16 @@ struct BGData struct VoidStorageItem { VoidStorageItem() : ItemId(0), ItemEntry(0), FixedScalingLevel(0), Context(ItemContext::NONE) { } - VoidStorageItem(uint64 id, uint32 entry, ObjectGuid const& creator, uint32 fixedScalingLevel, ItemContext context) : - ItemId(id), ItemEntry(entry), CreatorGuid(creator), FixedScalingLevel(fixedScalingLevel), Context(context) { } + VoidStorageItem(uint64 id, uint32 entry, ObjectGuid const& creator, uint32 fixedScalingLevel, ItemRandomProperties randomProperties, ItemContext context) : + ItemId(id), ItemEntry(entry), CreatorGuid(creator), FixedScalingLevel(fixedScalingLevel), RandomProperties(randomProperties), Context(context) { } VoidStorageItem(VoidStorageItem&& vsi) noexcept : ItemId(vsi.ItemId), ItemEntry(vsi.ItemEntry), CreatorGuid(vsi.CreatorGuid), - FixedScalingLevel(vsi.FixedScalingLevel), Context(vsi.Context) { } + FixedScalingLevel(vsi.FixedScalingLevel), RandomProperties(vsi.RandomProperties), Context(vsi.Context) { } uint64 ItemId; uint32 ItemEntry; ObjectGuid CreatorGuid; uint32 FixedScalingLevel; + ItemRandomProperties RandomProperties; ItemContext Context; }; @@ -1365,7 +1366,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasItemTotemCategory(uint32 TotemCategory) const; InventoryResult CanUseItem(ItemTemplate const* pItem, bool skipRequiredLevelCheck = false) const; InventoryResult CanRollNeedForItem(ItemTemplate const* item, Map const* map, bool restrictOnlyLfg) const; - Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, GuidSet const& allowedLooters = GuidSet(), + Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, ItemRandomProperties randomProperties = ItemRandomProperties(), GuidSet const& allowedLooters = GuidSet(), ItemContext context = ItemContext::NONE, bool addToCollection = true); Item* StoreItem(ItemPosCountVec const& pos, Item* pItem, bool update); Item* EquipNewItem(uint16 pos, uint32 item, ItemContext context, bool update); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1605dfc11cd..355f88be2c1 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3248,7 +3248,6 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.MaxMoneyLoot = 0; itemTemplate.FlagsCu = 0; itemTemplate.SpellPPMRate = 0.0f; - itemTemplate.RandomBonusListTemplateId = 0; itemTemplate.ItemSpecClassMask = 0; if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID)) @@ -3320,7 +3319,7 @@ void ObjectMgr::LoadItemTemplateAddon() uint32 oldMSTime = getMSTime(); uint32 count = 0; - QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId FROM item_template_addon"); + QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance FROM item_template_addon"); if (result) { do @@ -3346,7 +3345,6 @@ void ObjectMgr::LoadItemTemplateAddon() itemTemplate->MinMoneyLoot = minMoneyLoot; itemTemplate->MaxMoneyLoot = maxMoneyLoot; itemTemplate->SpellPPMRate = fields[5].GetFloat(); - itemTemplate->RandomBonusListTemplateId = fields[6].GetUInt32(); ++count; } while (result->NextRow()); } diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp index 7781fe83b85..6b2e939f2c6 100644 --- a/src/server/game/Guilds/GuildMgr.cpp +++ b/src/server/game/Guilds/GuildMgr.cpp @@ -431,7 +431,7 @@ void GuildMgr::LoadGuilds() do { Field* fields = result->Fetch(); - uint64 guildId = fields[44].GetUInt64(); + uint64 guildId = fields[46].GetUInt64(); if (Guild* guild = GetGuildById(guildId)) guild->LoadBankItemFromDB(fields); diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 45ac22bcef5..a6bbe5a38d8 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -462,7 +462,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem } // now move item from loot to target inventory - Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.GetAllowedLooters(), item.context); + Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomProperties, item.GetAllowedLooters(), item.context); aeResult.Add(newitem, item.count, loot->loot_type, loot->GetDungeonEncounterId()); // mark as looted diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index a108fcc9fae..5cd6cb11557 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -834,7 +834,7 @@ void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceRespons ItemPosCountVec dest; if (_player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.Id, item.Quantity) == EQUIP_ERR_OK) { - Item* newItem = _player->StoreNewItem(dest, item.Id, true, {}, ItemContext::Quest_Reward); + Item* newItem = _player->StoreNewItem(dest, item.Id, true, sItemEnchantmentMgr->GenerateRandomProperties(item.Id), {}, ItemContext::Quest_Reward); _player->SendNewItem(newItem, item.Quantity, true, false); } } diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp index d451b8a623e..24704d76c18 100644 --- a/src/server/game/Handlers/VoidStorageHandler.cpp +++ b/src/server/game/Handlers/VoidStorageHandler.cpp @@ -148,7 +148,8 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor continue; } - VoidStorageItem itemVS(sObjectMgr->GenerateVoidStorageItemId(), item->GetEntry(), item->GetCreator(), item->GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), item->GetContext()); + VoidStorageItem itemVS(sObjectMgr->GenerateVoidStorageItemId(), item->GetEntry(), item->GetCreator(), item->GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), + ItemRandomProperties(item->m_itemData->RandomPropertiesID, item->m_itemData->PropertySeed), item->GetContext()); WorldPackets::VoidStorage::VoidItem voidItem; voidItem.Guid = ObjectGuid::Create<HighGuid::Item>(itemVS.ItemId); @@ -185,7 +186,7 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor return; } - Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, GuidSet(), itemVS->Context); + Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, itemVS->RandomProperties, GuidSet(), itemVS->Context); item->SetCreator(itemVS->CreatorGuid); item->SetBinding(true); GetCollectionMgr()->AddItemAppearance(item); diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 2cc6f2b9017..02e8024221b 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -51,6 +51,7 @@ LootItem::LootItem(LootStoreItem const& li) needs_quest = li.needs_quest; + randomProperties = sItemEnchantmentMgr->GenerateRandomProperties(itemid); context = ItemContext::NONE; count = 0; is_looted = false; @@ -885,7 +886,7 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool --unlootedCount; - Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, GuidSet(), lootItem->context); + Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomProperties, GuidSet(), lootItem->context); player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast); player->ApplyItemLootedSpell(pItem, true); } diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index f985ca71486..33bee4568c8 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -176,6 +176,7 @@ struct TC_GAME_API LootItem { uint32 itemid; uint32 LootListId; + ItemRandomProperties randomProperties; ItemContext context; ConditionContainer conditions; // additional loot condition GuidSet allowedGUIDs; diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp index a580c3f3117..5953cd8a53d 100644 --- a/src/server/game/Loot/LootItemStorage.cpp +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -35,7 +35,7 @@ namespace StoredLootItem::StoredLootItem(LootItem const& lootItem) : ItemId(lootItem.itemid), Count(lootItem.count), ItemIndex(lootItem.LootListId), FollowRules(lootItem.follow_loot_rules), FFA(lootItem.freeforall), Blocked(lootItem.is_blocked), Counted(lootItem.is_counted), UnderThreshold(lootItem.is_underthreshold), -NeedsQuest(lootItem.needs_quest), Context(lootItem.context) +NeedsQuest(lootItem.needs_quest), RandomProperties(lootItem.randomProperties), Context(lootItem.context) { } @@ -79,7 +79,9 @@ void LootItemStorage::LoadStorageFromDB() lootItem.is_counted = fields[7].GetBool(); lootItem.is_underthreshold = fields[8].GetBool(); lootItem.needs_quest = fields[9].GetBool(); - lootItem.context = ItemContext(fields[10].GetUInt8()); + lootItem.randomProperties.RandomPropertiesID = fields[10].GetInt32(); + lootItem.randomProperties.RandomPropertiesSeed = fields[11].GetInt32(); + lootItem.context = ItemContext(fields[12].GetUInt8()); storedContainer.AddLootItem(lootItem, trans); @@ -146,6 +148,7 @@ bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) li.is_counted = storedItemPair.second.Counted; li.is_underthreshold = storedItemPair.second.UnderThreshold; li.needs_quest = storedItemPair.second.NeedsQuest; + li.randomProperties = storedItemPair.second.RandomProperties; li.context = storedItemPair.second.Context; // Copy the extra loot conditions from the item in the loot template @@ -276,7 +279,7 @@ void StoredLootContainer::AddLootItem(LootItem const& lootItem, CharacterDatabas CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_ITEMS); - // container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix + // container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, random_properties_id, random_properties_seed, context stmt->setUInt64(0, _containerId); stmt->setUInt32(1, lootItem.itemid); stmt->setUInt32(2, lootItem.count); @@ -287,7 +290,9 @@ void StoredLootContainer::AddLootItem(LootItem const& lootItem, CharacterDatabas stmt->setBool(7, lootItem.is_counted); stmt->setBool(8, lootItem.is_underthreshold); stmt->setBool(9, lootItem.needs_quest); - stmt->setUInt8(10, AsUnderlyingType(lootItem.context)); + stmt->setInt32(9, lootItem.randomProperties.RandomPropertiesID); + stmt->setInt32(10, lootItem.randomProperties.RandomPropertiesSeed); + stmt->setUInt8(11, AsUnderlyingType(lootItem.context)); trans->Append(stmt); } diff --git a/src/server/game/Loot/LootItemStorage.h b/src/server/game/Loot/LootItemStorage.h index bd96cc90588..21bcfe6feec 100644 --- a/src/server/game/Loot/LootItemStorage.h +++ b/src/server/game/Loot/LootItemStorage.h @@ -45,6 +45,7 @@ struct StoredLootItem bool Counted; bool UnderThreshold; bool NeedsQuest; + ItemRandomProperties RandomProperties; ItemContext Context; }; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d6a7e320cd1..4984c56ab60 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1365,7 +1365,7 @@ void Spell::DoCreateItem(uint32 itemId, ItemContext context /*= ItemContext::NON if (num_to_add) { // create the new item and store it - Item* pItem = player->StoreNewItem(dest, newitemid, true, GuidSet(), context); + Item* pItem = player->StoreNewItem(dest, newitemid, true, sItemEnchantmentMgr->GenerateRandomProperties(newitemid), GuidSet(), context); // was it successful? return error if not if (!pItem) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 581578ebc10..eba12d8a794 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -550,7 +550,7 @@ int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 return int32(round(value)); } -int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const +int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 /*itemLevel*/) const { if (Scaling.Coefficient != 0.0f) { @@ -575,6 +575,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ if (!Scaling.Class) return 0; + /* uint32 effectiveItemLevel = itemLevel != -1 ? uint32(itemLevel) : 1u; if (_spellInfo->Scaling.ScalesFromItemLevel || _spellInfo->HasAttribute(SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL)) { @@ -594,6 +595,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ } else value = GetSpellScalingColumnForClass(sSpellScalingGameTable.GetRow(level), Scaling.Class); + */ } value *= Scaling.Coefficient; @@ -4250,12 +4252,15 @@ inline float CalcPPMItemLevelMod(SpellProcsPerMinuteModEntry const* mod, int32 i if (itemLevel == mod->Param) return 0.0f; + /* float itemLevelPoints = GetRandomPropertyPoints(itemLevel, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); float basePoints = GetRandomPropertyPoints(mod->Param, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); if (itemLevelPoints == basePoints) return 0.0f; return ((itemLevelPoints / basePoints) - 1.0f) * mod->Coeff; + */ + return 0; } float SpellInfo::CalcProcPPM(Unit* caster, int32 itemLevel) const diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c24e120b13f..3bb2165bda1 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1963,6 +1963,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Disables"); // must be before loading quests and items DisableMgr::LoadDisables(); + TC_LOG_INFO("server.loading", "Loading Item Random Enchantments Table..."); + sItemEnchantmentMgr->LoadRandomEnchantmentsTable(); + TC_LOG_INFO("server.loading", "Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts sObjectMgr->LoadItemTemplates(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 12b08a05c6d..3d1030eda32 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1290,7 +1290,7 @@ public: return false; } - Item* item = playerTarget->StoreNewItem(dest, itemId, true, GuidSet(), itemContext); + Item* item = playerTarget->StoreNewItem(dest, itemId, true, sItemEnchantmentMgr->GenerateRandomProperties(itemId), GuidSet(), itemContext); // remove binding (let GM give it to another player later) if (player == playerTarget) @@ -1443,7 +1443,7 @@ public: return false; } - Item* item = playerTarget->StoreNewItem(dest, itemId, true, GuidSet(), itemContext); + Item* item = playerTarget->StoreNewItem(dest, itemId, true, sItemEnchantmentMgr->GenerateRandomProperties(itemId), GuidSet(), itemContext); // remove binding (let GM give it to another player later) if (player == playerTarget) @@ -1497,7 +1497,7 @@ public: InventoryResult msg = playerTarget->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemTemplatePair.first, 1); if (msg == EQUIP_ERR_OK) { - Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, GuidSet(), itemContext); + Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, sItemEnchantmentMgr->GenerateRandomProperties(itemTemplatePair.first), GuidSet(), itemContext); // remove binding (let GM give it to another player later) if (player == playerTarget) |