diff options
Diffstat (limited to 'src')
26 files changed, 423 insertions, 105 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 1462006e552..1a69c73fd21 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.randomBonusListId, " \ - "ii.durability, ii.playedTime, ii.createTime, ii.text, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.context, ii.bonusListIDs, " \ + "ii.durability, ii.playedTime, ii.createTime, ii.text, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.context, ii.bonusListIDs, ii.randomPropertiesId, " \ "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.secondaryItemModifiedAppearanceSpec5, " \ @@ -192,8 +192,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, randomBonusListId, durability, playedTime, createTime, text, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomBonusListId = ?, durability = ?, playedTime = ?, createTime = ?, text = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, context = ?, bonusListIDs = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomBonusListId, durability, playedTime, createTime, text, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, randomPropertiesId, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomBonusListId = ?, durability = ?, playedTime = ?, createTime = ?, text = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, context = ?, bonusListIDs = ?, randomPropertiesId = ? 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); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 9fb645bc571..d79d35a1d97 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -722,6 +722,20 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_ITEM_PRICE_BASE, "SELECT ID, ItemLevel, Armor, Weapon FROM item_price_base WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_PRICE_BASE, "SELECT MAX(ID) + 1 FROM item_price_base", CONNECTION_SYNCH); + // ItemRandomProperties.db2 + PrepareStatement(HOTFIX_SEL_ITEM_RANDOM_PROPERTIES, "SELECT ID, Name, Enchantment1, Enchantment2, Enchantment3, Enchantment4, Enchantment5" + " FROM item_random_properties WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_RANDOM_PROPERTIES, "SELECT MAX(ID) + 1 FROM item_random_properties", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_RANDOM_PROPERTIES, "SELECT ID, Name_lang FROM item_random_properties_locale WHERE (`VerifiedBuild` > 0) = ?" + " AND locale = ?", CONNECTION_SYNCH); + + // ItemRandomSuffix.db2 + PrepareStatement(HOTFIX_SEL_ITEM_RANDOM_SUFFIX, "SELECT ID, Name, Enchantment1, Enchantment2, Enchantment3, Enchantment4, Enchantment5, " + "AllocationPct1, AllocationPct2, AllocationPct3, AllocationPct4, AllocationPct5 FROM item_random_suffix WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_RANDOM_SUFFIX, "SELECT MAX(ID) + 1 FROM item_random_suffix", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_RANDOM_SUFFIX, "SELECT ID, Name_lang FROM item_random_suffix_locale WHERE (`VerifiedBuild` > 0) = ?" + " AND locale = ?", CONNECTION_SYNCH); + // ItemReforge.db2 PrepareStatement(HOTFIX_SEL_ITEM_REFORGE, "SELECT ID, SourceStat, SourceMultiplier, TargetStat, TargetMultiplier, LegacyItemReforgeID" " FROM item_reforge WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index b28d27eadc4..2e064748e4a 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -422,6 +422,14 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_ITEM_PRICE_BASE, HOTFIX_SEL_ITEM_PRICE_BASE_MAX_ID, + HOTFIX_SEL_ITEM_RANDOM_PROPERTIES, + HOTFIX_SEL_ITEM_RANDOM_PROPERTIES_MAX_ID, + HOTFIX_SEL_ITEM_RANDOM_PROPERTIES_LOCALE, + + HOTFIX_SEL_ITEM_RANDOM_SUFFIX, + HOTFIX_SEL_ITEM_RANDOM_SUFFIX_MAX_ID, + HOTFIX_SEL_ITEM_RANDOM_SUFFIX_LOCALE, + HOTFIX_SEL_ITEM_REFORGE, HOTFIX_SEL_ITEM_REFORGE_MAX_ID, diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 1e512a98a97..bcbb5c4ad26 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -528,12 +528,12 @@ void AuctionHouseMgr::LoadAuctions() } Item* item = NewItemOrBag(proto); - if (!item->LoadFromDB(itemGuid, ObjectGuid::Create<HighGuid::Player>(fields[53].GetUInt64()), fields, itemEntry)) + if (!item->LoadFromDB(itemGuid, ObjectGuid::Create<HighGuid::Player>(fields[54].GetUInt64()), fields, itemEntry)) { delete item; continue; } - uint32 auctionId = fields[54].GetUInt32(); + uint32 auctionId = fields[55].GetUInt32(); itemsByAuction[auctionId].push_back(item); ++count; diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp index 478e7e19b55..cd8b1fc6ad9 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp @@ -868,6 +868,7 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) // Update the just created item so that if it needs random properties it has them. // Ex: Notched Shortsword of Stamina will only generate as a Notched Shortsword without this. item->SetItemRandomBonusList(GenerateItemRandomBonusListId(itemId)); + item->SetItemRandomEnchantment(GenerateItemRandomPropertiesId(itemId)); uint32 buyoutPrice; uint32 bidPrice = 0; diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 3d3a3ea7604..523f029f80d 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -2265,6 +2265,43 @@ struct ItemPriceBaseLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 4, &ItemPriceBaseMeta::Instance, HOTFIX_SEL_ITEM_PRICE_BASE }; }; +struct ItemRandomPropertiesLoadInfo +{ + static constexpr DB2FieldMeta Fields[7] = + { + { false, FT_INT, "ID" }, + { false, FT_STRING, "Name" }, + { true, FT_INT, "Enchantment1" }, + { true, FT_INT, "Enchantment2" }, + { true, FT_INT, "Enchantment3" }, + { true, FT_INT, "Enchantment4" }, + { true, FT_INT, "Enchantment5" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 7, &ItemRandomPropertiesMeta::Instance, HOTFIX_SEL_ITEM_RANDOM_PROPERTIES }; +}; + +struct ItemRandomSuffixLoadInfo +{ + static constexpr DB2FieldMeta Fields[12] = + { + { false, FT_INT, "ID" }, + { false, FT_STRING, "Name" }, + { true, FT_INT, "Enchantment1" }, + { true, FT_INT, "Enchantment2" }, + { true, FT_INT, "Enchantment3" }, + { true, FT_INT, "Enchantment4" }, + { true, FT_INT, "Enchantment5" }, + { true, FT_INT, "AllocationPct1" }, + { true, FT_INT, "AllocationPct2" }, + { true, FT_INT, "AllocationPct3" }, + { true, FT_INT, "AllocationPct4" }, + { true, FT_INT, "AllocationPct5" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 12, &ItemRandomSuffixMeta::Instance, HOTFIX_SEL_ITEM_RANDOM_SUFFIX }; +}; + struct ItemReforgeLoadInfo { static constexpr DB2FieldMeta Fields[6] = @@ -3720,16 +3757,6 @@ struct ScalingStatDistributionLoadInfo { false, FT_SHORT, "PlayerLevelToItemLevelCurveID" }, { true, FT_INT, "Minlevel" }, { true, FT_INT, "Maxlevel" }, - { true, FT_INT, "StatID1" }, - { true, FT_INT, "StatID2" }, - { true, FT_INT, "StatID3" }, - { true, FT_INT, "StatID4" }, - { true, FT_INT, "StatID5" }, - { true, FT_INT, "StatID6" }, - { true, FT_INT, "StatID7" }, - { true, FT_INT, "StatID8" }, - { true, FT_INT, "StatID9" }, - { true, FT_INT, "StatID10" }, { true, FT_INT, "Bonus1" }, { true, FT_INT, "Bonus2" }, { true, FT_INT, "Bonus3" }, @@ -3740,6 +3767,16 @@ struct ScalingStatDistributionLoadInfo { true, FT_INT, "Bonus8" }, { true, FT_INT, "Bonus9" }, { true, FT_INT, "Bonus10" }, + { true, FT_INT, "StatID1" }, + { true, FT_INT, "StatID2" }, + { true, FT_INT, "StatID3" }, + { true, FT_INT, "StatID4" }, + { true, FT_INT, "StatID5" }, + { true, FT_INT, "StatID6" }, + { true, FT_INT, "StatID7" }, + { true, FT_INT, "StatID8" }, + { true, FT_INT, "StatID9" }, + { true, FT_INT, "StatID10" }, }; static constexpr DB2LoadInfo Instance{ Fields, 24, &ScalingStatDistributionMeta::Instance, HOTFIX_SEL_SCALING_STAT_DISTRIBUTION }; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 7cd81ae8799..8462613d4ce 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -157,6 +157,8 @@ DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore("It DB2Storage<ItemModifiedAppearanceExtraEntry> sItemModifiedAppearanceExtraStore("ItemModifiedAppearanceExtra.db2", &ItemModifiedAppearanceExtraLoadInfo::Instance); DB2Storage<ItemNameDescriptionEntry> sItemNameDescriptionStore("ItemNameDescription.db2", &ItemNameDescriptionLoadInfo::Instance); DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore("ItemPriceBase.db2", &ItemPriceBaseLoadInfo::Instance); +DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore("ItemRandomProperties.db2", &ItemRandomPropertiesLoadInfo::Instance); +DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore("ItemRandomSuffix.db2", &ItemRandomSuffixLoadInfo::Instance); DB2Storage<ItemReforgeEntry> sItemReforgeStore("ItemReforge.db2", &ItemReforgeLoadInfo::Instance); DB2Storage<ItemSearchNameEntry> sItemSearchNameStore("ItemSearchName.db2", &ItemSearchNameLoadInfo::Instance); DB2Storage<ItemSetEntry> sItemSetStore("ItemSet.db2", &ItemSetLoadInfo::Instance); @@ -662,6 +664,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(sItemReforgeStore); LOAD_DB2(sItemSearchNameStore); LOAD_DB2(sItemSetStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 34803198506..e46fe813214 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -128,6 +128,8 @@ TC_GAME_API extern DB2Storage<ItemLimitCategoryEntry> sItemLimitCa TC_GAME_API extern DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore; TC_GAME_API extern DB2Storage<ItemModifiedAppearanceExtraEntry> sItemModifiedAppearanceExtraStore; TC_GAME_API extern DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore; +TC_GAME_API extern DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore; +TC_GAME_API extern DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore; TC_GAME_API extern DB2Storage<ItemReforgeEntry> sItemReforgeStore; TC_GAME_API extern DB2Storage<ItemSearchNameEntry> sItemSearchNameStore; TC_GAME_API extern DB2Storage<ItemSetEntry> sItemSetStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 836163b7f3b..56d713c1a68 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1912,6 +1912,23 @@ struct ItemPriceBaseEntry float Weapon; }; +// structure for ItemRandomProperties.db2 +struct ItemRandomPropertiesEntry +{ + uint32 ID; + LocalizedString Name; + std::array<int32, 5> Enchantment; +}; + +// structure for ItemRandomSuffix.db2 +struct ItemRandomSuffixEntry +{ + uint32 ID; + LocalizedString Name; + std::array<int32, 5> Enchantment; + std::array<int32, 5> AllocationPct; +}; + // structure for ItemReforge.db2 struct ItemReforgeEntry { diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index dae8436cbe3..7c6f7de62ef 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -489,6 +489,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans) bonusListIDs << bonusListID << ' '; stmt->setString(++index, bonusListIDs.str()); + stmt->setInt32(++index, m_itemData->RandomPropertiesID); stmt->setUInt64(++index, GetGUID().GetCounter()); trans->Append(stmt); @@ -670,19 +671,19 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomBonusListId, durability, playedTime, createTime, text, - // 14 15 16 17 18 19 - // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, - // 20 21 22 23 24 25 + // 14 15 16 17 18 19 20 + // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, randomPropertiesId, + // 21 22 23 24 25 26 // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, itemModifiedAppearanceSpec5, - // 26 27 28 29 30 31 + // 27 28 29 30 31 32 // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, spellItemEnchantmentSpec5, - // 32 33 34 + // 33 34 35 // secondaryItemModifiedAppearanceAllSpecs, secondaryItemModifiedAppearanceSpec1, secondaryItemModifiedAppearanceSpec2, - // 35 36 37 + // 36 37 38 // secondaryItemModifiedAppearanceSpec3, secondaryItemModifiedAppearanceSpec4, secondaryItemModifiedAppearanceSpec5, - // 38 39 40 41 42 43 44 45 46 47 48 49 + // 39 40 41 42 43 44 45 46 47 48 49 50 // gemItemId1, gemBonuses1, gemContext1, gemScalingLevel1, gemItemId2, gemBonuses2, gemContext2, gemScalingLevel2, gemItemId3, gemBonuses3, gemContext3, gemScalingLevel3 - // 50 51 52 + // 51 52 53 // fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId FROM item_instance // create item before any checks for store correct guid @@ -761,52 +762,59 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie bonusListIDs.push_back(*bonusListID); SetBonuses(std::move(bonusListIDs)); + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::RandomPropertiesID), fields[20].GetInt32()); + if (m_itemData->RandomPropertiesID < 0) + { + int32 propertySeed = static_cast<int32>(GenerateEnchSuffixFactor(GetEntry())); + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::PropertySeed), propertySeed); + } + // 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) SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::SpellCharges, i), Trinity::StringTo<int32>(tokens[i]).value_or(0)); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[20].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[21].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[22].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[23].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[24].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_5, fields[25].GetUInt32()); - - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[26].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[27].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[28].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[29].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[30].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_5, fields[31].GetUInt32()); - - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, fields[32].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1, fields[33].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2, fields[34].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3, fields[35].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4, fields[36].GetUInt32()); - SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_5, fields[37].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[21].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[22].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[23].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[24].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[25].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_5, fields[26].GetUInt32()); + + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[27].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[28].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[29].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[30].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[31].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_5, fields[32].GetUInt32()); + + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, fields[33].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1, fields[34].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2, fields[35].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3, fields[36].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4, fields[37].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_5, fields[38].GetUInt32()); uint32 const gemFields = 4; ItemDynamicFieldGems gemData[MAX_GEM_SOCKETS]; memset(gemData, 0, sizeof(gemData)); for (uint32 i = 0; i < MAX_GEM_SOCKETS; ++i) { - gemData[i].ItemId = fields[38 + i * gemFields].GetUInt32(); - std::vector<std::string_view> gemBonusListIDs = Trinity::Tokenize(fields[39 + i * gemFields].GetStringView(), ' ', false); + gemData[i].ItemId = fields[39 + i * gemFields].GetUInt32(); + std::vector<std::string_view> gemBonusListIDs = Trinity::Tokenize(fields[40 + 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[40 + i * gemFields].GetUInt8(); + gemData[i].Context = fields[41 + i * gemFields].GetUInt8(); if (gemData[i].ItemId) - SetGem(i, &gemData[i], fields[41 + i * gemFields].GetUInt32()); + SetGem(i, &gemData[i], fields[42 + i * gemFields].GetUInt32()); } - SetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL, fields[50].GetUInt32()); - SetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, fields[51].GetUInt32()); - SetModifier(ITEM_MODIFIER_REFORGE, fields[52].GetUInt32()); + SetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL, fields[51].GetUInt32()); + SetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, fields[52].GetUInt32()); + SetModifier(ITEM_MODIFIER_REFORGE, fields[53].GetUInt32()); // Enchants must be loaded after all other bonus/scaling data std::vector<std::string_view> enchantmentTokens = Trinity::Tokenize(fields[8].GetStringView(), ' ', false); @@ -914,6 +922,47 @@ void Item::SetItemRandomBonusList(ItemRandomBonusListId bonusListId) AddBonuses(bonusListId); } +void Item::SetItemRandomEnchantment(ItemRandomPropertiesId randomPropertiesId) +{ + if (!randomPropertiesId) + return; + + if (randomPropertiesId > 0) + { + // > 0 implies it's a random properties Id + if (ItemRandomPropertiesEntry const* randomProperties = sItemRandomPropertiesStore.LookupEntry(randomPropertiesId)) + { + if (m_itemData->RandomPropertiesID != randomPropertiesId) + { + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::RandomPropertiesID), randomPropertiesId); + SetState(ITEM_CHANGED, GetOwner()); + } + + for (uint16 i = PROP_ENCHANTMENT_SLOT_0; i <= PROP_ENCHANTMENT_SLOT_4; ++i) + SetEnchantment(EnchantmentSlot(i), randomProperties->Enchantment[i - PROP_ENCHANTMENT_SLOT_0], 0, 0); + } + } + else + { + // < 0 implies it's a random Suffix + if (ItemRandomSuffixEntry const* randomSuffix = sItemRandomSuffixStore.LookupEntry(std::abs(randomPropertiesId))) + { + if (m_itemData->RandomPropertiesID != randomPropertiesId) + { + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::RandomPropertiesID), randomPropertiesId); + int32 propertySeed = static_cast<int32>(GenerateEnchSuffixFactor(GetEntry())); + if (m_itemData->PropertySeed != propertySeed) + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::PropertySeed), propertySeed); + + SetState(ITEM_CHANGED, GetOwner()); + } + + for (uint16 i = PROP_ENCHANTMENT_SLOT_0; i <= PROP_ENCHANTMENT_SLOT_4; ++i) + SetEnchantment(EnchantmentSlot(i), randomSuffix->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 7b3f18c4dff..6ed742ef011 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -237,6 +237,7 @@ class TC_GAME_API Item : public Object ItemRandomBonusListId GetItemRandomBonusListId() const { return m_randomBonusListId; } void SetItemRandomBonusList(ItemRandomBonusListId bonusListId); + void SetItemRandomEnchantment(ItemRandomPropertiesId randomEnchantmentId); 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 224a8cc6001..df142c97c8a 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp @@ -27,6 +27,12 @@ #include <list> #include <vector> +enum class ItemRandomEnchantmentType : uint8 +{ + Property = 0, + Suffix = 1 +}; + namespace { struct RandomBonusListIds @@ -35,14 +41,22 @@ namespace std::vector<double> Chances; }; - std::unordered_map<uint32, RandomBonusListIds> _storage; + struct RandomEnchantmentIds + { + std::vector<int32> EnchantmentIDs; + std::vector<double> Chances; + }; + + std::unordered_map<uint32, RandomBonusListIds> _bonusListStorage; + std::unordered_map<uint32, RandomEnchantmentIds> _randomPropertiesStorage; + std::unordered_map<uint32, RandomEnchantmentIds> _randomSuffixStorage; } void LoadItemRandomBonusListTemplates() { uint32 oldMSTime = getMSTime(); - _storage.clear(); + _bonusListStorage.clear(); // 0 1 2 QueryResult result = WorldDatabase.Query("SELECT Id, BonusListID, Chance FROM item_random_bonus_list_template"); @@ -61,7 +75,7 @@ void LoadItemRandomBonusListTemplates() if (ItemBonusMgr::GetItemBonuses(bonusListId).empty()) { - TC_LOG_ERROR("sql.sql", "Bonus list {} used in `item_random_bonus_list_template` by id {} doesn't have exist in ItemBonus.db2", bonusListId, id); + TC_LOG_ERROR("sql.sql", "Bonus list {} used in `item_random_bonus_list_template` by id {} doesn't exist in ItemBonus.db2", bonusListId, id); continue; } @@ -71,7 +85,7 @@ void LoadItemRandomBonusListTemplates() continue; } - RandomBonusListIds& ids = _storage[id]; + RandomBonusListIds& ids = _bonusListStorage[id]; ids.BonusListIDs.push_back(bonusListId); ids.Chances.push_back(chance); @@ -84,6 +98,86 @@ void LoadItemRandomBonusListTemplates() TC_LOG_INFO("server.loading", ">> Loaded 0 Random item bonus list definitions. DB table `item_random_bonus_list_template` is empty."); } +void LoadItemRandomEnchantmentsTemplates() +{ + uint32 oldMSTime = getMSTime(); + + _randomPropertiesStorage.clear(); + _randomSuffixStorage.clear(); + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT Id, EnchantmentType, EnchantmentId, Chance FROM item_random_enchantment_template"); + + if (result) + { + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 Id = fields[0].GetUInt32(); + ItemRandomEnchantmentType enchantmentType = static_cast<ItemRandomEnchantmentType>(fields[1].GetUInt8()); + uint32 enchantmentId = fields[2].GetUInt32(); + float chance = fields[3].GetFloat(); + + switch (enchantmentType) + { + case ItemRandomEnchantmentType::Property: + if (!sItemRandomPropertiesStore.HasRecord(enchantmentId)) + { + TC_LOG_ERROR("sql.sql", "Random properties Id {} for Id {} defined in `item_random_enchantment_template` doesn't exist in ItemRandomProperties.db2", enchantmentId, Id); + continue; + } + break; + case ItemRandomEnchantmentType::Suffix: + if (!sItemRandomSuffixStore.HasRecord(enchantmentId)) + { + TC_LOG_ERROR("sql.sql", "Random suffix Id {} for Id {} defined in `item_random_enchantment_template` doesn't exist in ItemRandomSuffix.db2", enchantmentId, Id); + continue; + } + break; + default: + TC_LOG_ERROR("sql.sql", "Invalid random enchantment type specified in `item_random_enchantment_template` table for `Id` {} `EnchantmentId` {}", Id, enchantmentId); + break; + } + + if (chance < 0.000001f || chance > 100.0f) + { + TC_LOG_ERROR("sql.sql", "Random enchantment Id {} used in `item_random_enchantment_template` by Id {} and Type {} has an invalid chance {}", enchantmentId, Id, AsUnderlyingType(enchantmentType), chance); + continue; + } + + switch (enchantmentType) + { + case ItemRandomEnchantmentType::Property: + { + RandomEnchantmentIds& randomProperties = _randomPropertiesStorage[Id]; + randomProperties.EnchantmentIDs.push_back(enchantmentId); + randomProperties.Chances.push_back(chance); + break; + } + case ItemRandomEnchantmentType::Suffix: + { + RandomEnchantmentIds& randomSuffix = _randomSuffixStorage[Id]; + randomSuffix.EnchantmentIDs.push_back(enchantmentId); + randomSuffix.Chances.push_back(chance); + break; + } + default: + break; + } + + ++count; + + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded {} Random item random enchantment definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + else + TC_LOG_INFO("server.loading", ">> Loaded 0 Random item random enchantment definitions. DB table `item_random_enchantment_template` is empty."); + +} + ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id) { ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_id); @@ -94,8 +188,8 @@ ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id) if (!itemProto->RandomBonusListTemplateId) return 0; - auto tab = _storage.find(itemProto->RandomBonusListTemplateId); - if (tab == _storage.end()) + auto tab = _bonusListStorage.find(itemProto->RandomBonusListTemplateId); + if (tab == _bonusListStorage.end()) { 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; @@ -104,9 +198,64 @@ ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id) return *Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.BonusListIDs, std::span(tab->second.Chances)); } -TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subClass) +TC_GAME_API ItemRandomPropertiesId GenerateItemRandomPropertiesId(uint32 item_id) +{ + ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_id); + if (!itemProto) + return 0; + + uint16 randomSelect = itemProto->GetRandomSelect(); + uint16 randomSuffix = itemProto->GetItemRandomSuffixGroupID(); + + if (!randomSelect && !randomSuffix) + return 0; + + // An item cannot have random properties and suffix at the same time + if (randomSelect && randomSuffix) + { + TC_LOG_ERROR("sql.sql", "Item (Id: {} has RandomSelect and ItemRandomSuffixGroupID values which is not allowed!", itemProto->GetId()); + return 0; + } + + if (randomSelect) + { + auto tab = _randomPropertiesStorage.find(randomSelect); + if (tab == _randomPropertiesStorage.end()) + { + TC_LOG_ERROR("sql.sql", "RandomSelect Id {} used does not have any data defined in `item_random_enchantment_template`tabel.", itemProto->GetRandomSelect()); + return 0; + } + + return *Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.EnchantmentIDs, std::span(tab->second.Chances)); + } + + if (randomSuffix) + { + auto tab = _randomSuffixStorage.find(randomSuffix); + if (tab == _randomSuffixStorage.end()) + { + TC_LOG_ERROR("sql.sql", "RandomSuffixGroup Id {} used does not have any data defined in `item_random_enchantment_template`tabel.", itemProto->GetRandomSelect()); + return 0; + } + + return -*Trinity::Containers::SelectRandomWeightedContainerElement(tab->second.EnchantmentIDs, std::span(tab->second.Chances)); + } + + return 0; +} + +TC_GAME_API uint32 GenerateEnchSuffixFactor(uint32 item_id) +{ + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item_id); + if (!proto || !proto->GetItemRandomSuffixGroupID()) + return 0; + + return GetRandomPropertyPoints(proto->GetBaseItemLevel(), proto->GetQuality(), proto->GetInventoryType(), proto->GetSubClass()); +} + +TC_GAME_API uint32 GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subClass) { - uint32 propIndex; + uint32 propIndex = 0; switch (inventoryType) { diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.h b/src/server/game/Entities/Item/ItemEnchantmentMgr.h index 5c8f524a125..73fa04535a0 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.h +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.h @@ -21,9 +21,13 @@ #include "Common.h" using ItemRandomBonusListId = uint32; +using ItemRandomPropertiesId = int32; TC_GAME_API void LoadItemRandomBonusListTemplates(); +TC_GAME_API void LoadItemRandomEnchantmentsTemplates(); TC_GAME_API ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id); -TC_GAME_API float GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subclass); +TC_GAME_API ItemRandomPropertiesId GenerateItemRandomPropertiesId(uint32 item_id); +TC_GAME_API uint32 GenerateEnchSuffixFactor(uint32 item_id); +TC_GAME_API uint32 GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uint32 inventoryType, uint32 subclass); #endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 295656bce9e..1f068a0ef96 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -585,7 +585,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, GenerateItemRandomBonusListId(itemId), GuidSet(), context); + StoreNewItem(sDest, itemId, true, GenerateItemRandomBonusListId(itemId), GenerateItemRandomPropertiesId(itemId), GuidSet(), context); return true; // stored } @@ -3526,7 +3526,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe do { Field* fields = resultItems->Fetch(); - uint64 mailId = fields[54].GetUInt64(); + uint64 mailId = fields[55].GetUInt64(); if (Item* mailItem = _LoadMailedItem(playerguid, nullptr, mailId, nullptr, fields)) itemsByMail[mailId].push_back(mailItem); @@ -10685,7 +10685,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, ItemRandomBonusListId randomBonusListId /*= 0*/, +Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, ItemRandomBonusListId randomBonusListId /*= 0*/, ItemRandomPropertiesId randomEnchantmentId /*= 0*/, GuidSet const& allowedLooters /*= GuidSet()*/, ItemContext context /*= ItemContext::NONE*/, std::vector<int32> const* bonusListIDs /*= std::vector<int32>()*/, bool addToCollection /*= true*/) { @@ -10703,6 +10703,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat item->SetFixedLevel(GetLevel()); item->SetItemRandomBonusList(randomBonusListId); + item->SetItemRandomEnchantment(randomEnchantmentId); item->SetCreatePlayedTime(GetTotalPlayedTime()); item = StoreItem(pos, item, update); @@ -10738,7 +10739,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat { ItemPosCountVec childDest; CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, childDest, childTemplate, count, false, nullptr, NULL_BAG, NULL_SLOT); - if (Item* childItem = StoreNewItem(childDest, childTemplate->GetId(), update, {}, {}, context, nullptr, addToCollection)) + if (Item* childItem = StoreNewItem(childDest, childTemplate->GetId(), update, {}, {}, {}, context, nullptr, addToCollection)) { childItem->SetCreator(item->GetGUID()); childItem->SetItemFlag(ITEM_FIELD_FLAG_CHILD); @@ -12917,8 +12918,23 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool for (int s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s) { uint32 enchant_display_type = pEnchant->Effect[s]; - uint32 enchant_amount = pEnchant->EffectPointsMin[s]; uint32 enchant_spell_id = pEnchant->EffectArg[s]; + uint32 enchant_amount = pEnchant->EffectPointsMin[s];; + + if (enchant_amount == 0 && slot >= PROP_ENCHANTMENT_SLOT_0 && slot <= PROP_ENCHANTMENT_SLOT_4) + { + if (ItemRandomSuffixEntry const* suffix = sItemRandomSuffixStore.LookupEntry(std::abs(item->m_itemData->RandomPropertiesID))) + { + for (size_t i = 0; i < suffix->Enchantment.size(); ++i) + { + if (suffix->Enchantment[i] == static_cast<int32>(enchant_id)) + { + enchant_amount = suffix->AllocationPct[i] * item->m_itemData->PropertySeed / 10000; + break; + } + } + } + } switch (enchant_display_type) { @@ -14223,7 +14239,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, GenerateItemRandomBonusListId(questPackageItem->ItemID), {}, context); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomBonusListId(questPackageItem->ItemID), GenerateItemRandomPropertiesId(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -14242,7 +14258,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, GenerateItemRandomBonusListId(questPackageItem->ItemID), {}, context); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomBonusListId(questPackageItem->ItemID), GenerateItemRandomPropertiesId(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -14302,7 +14318,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, GenerateItemRandomBonusListId(itemId), {}, ItemContext::Quest_Reward); + Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GenerateItemRandomPropertiesId(itemId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardItemCount[i], true, false); } else if (quest->IsDFQuest()) @@ -14347,7 +14363,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, GenerateItemRandomBonusListId(rewardId), {}, ItemContext::Quest_Reward); + Item* item = StoreNewItem(dest, rewardId, true, GenerateItemRandomBonusListId(rewardId), GenerateItemRandomPropertiesId(rewardId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardChoiceItemCount[i], true, false); } } @@ -17855,21 +17871,21 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomBonusListId, durability, playedTime, createTime, text, - // 14 15 16 17 18 19 - // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, - // 20 21 22 23 24 25 + // 14 15 16 17 18 19 20 + // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, randomPropertiesId, + // 21 22 23 24 25 26 // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, itemModifiedAppearanceSpec5, - // 26 27 28 29 30 31 + // 27 28 29 30 31 32 // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, spellItemEnchantmentSpec5, - // 32 33 34 + // 33 34 35 // secondaryItemModifiedAppearanceAllSpecs, secondaryItemModifiedAppearanceSpec1, secondaryItemModifiedAppearanceSpec2, - // 35 36 37 + // 36 37 38 // secondaryItemModifiedAppearanceSpec3, secondaryItemModifiedAppearanceSpec4, secondaryItemModifiedAppearanceSpec5, - // 38 39 40 41 42 43 44 45 46 47 48 49 + // 39 40 41 42 43 44 45 46 47 48 49 50 // gemItemId1, gemBonuses1, gemContext1, gemScalingLevel1, gemItemId2, gemBonuses2, gemContext2, gemScalingLevel2, gemItemId3, gemBonuses3, gemContext3, gemScalingLevel3 - // 50 51 52 + // 51 52 53 // fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId FROM item_instance - // 53 54 + // 54 55 // bag, slot // FROM character_inventory ci // JOIN item_instance ii ON ci.item = ii.guid @@ -17901,8 +17917,8 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) Field* fields = result->Fetch(); if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields)) { - ObjectGuid bagGuid = fields[53].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[53].GetUInt64()) : ObjectGuid::Empty; - uint8 slot = fields[54].GetUInt8(); + ObjectGuid bagGuid = fields[54].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[53].GetUInt64()) : ObjectGuid::Empty; + uint8 slot = fields[55].GetUInt8(); GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item); GetSession()->GetCollectionMgr()->AddItemAppearance(item); @@ -18233,7 +18249,7 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint Item* item = NewItemOrBag(proto); - ObjectGuid ownerGuid = fields[53].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[53].GetUInt64()) : ObjectGuid::Empty; + ObjectGuid ownerGuid = fields[54].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[54].GetUInt64()) : ObjectGuid::Empty; if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry)) { TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: {}) in mail ({}) doesn't exist, deleted from mail.", itemGuid, mailId); @@ -18302,7 +18318,7 @@ void Player::_LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mail do { Field* fields = mailItemsResult->Fetch(); - uint64 mailId = fields[54].GetUInt64(); + uint64 mailId = fields[55].GetUInt64(); _LoadMailedItem(GetGUID(), this, mailId, mailById[mailId], fields); } while (mailItemsResult->NextRow()); } @@ -21924,7 +21940,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c } Item* it = bStore ? - StoreNewItem(vDest, item, true, GenerateItemRandomBonusListId(item), {}, ItemContext::Vendor, &crItem->BonusListIDs, false) : + StoreNewItem(vDest, item, true, GenerateItemRandomBonusListId(item), GenerateItemRandomPropertiesId(item), {}, ItemContext::Vendor, &crItem->BonusListIDs, false) : EquipNewItem(uiDest, item, ItemContext::Vendor, true); if (it) { @@ -25573,7 +25589,7 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* return; } - Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomBonusListId, item->GetAllowedLooters(), item->context); + Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomBonusListId, item->randomPropertiesId, item->GetAllowedLooters(), item->context); if (sObjectMgr->GetItemTemplate(item->itemid)) if (newitem->GetQuality() > ITEM_QUALITY_EPIC || (newitem->GetQuality() == ITEM_QUALITY_EPIC && newitem->GetItemLevel(this) >= MinNewsItemLevel)) @@ -26658,7 +26674,7 @@ bool Player::AddItem(uint32 itemId, uint32 count) return false; } - Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId)); + Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GenerateItemRandomPropertiesId(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 698d8eff36c..32ba39828c5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1403,7 +1403,7 @@ class TC_GAME_API Player final : 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, ItemRandomBonusListId randomBonusListId = 0, GuidSet const& allowedLooters = GuidSet(), + Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, ItemRandomBonusListId randomBonusListId = 0, ItemRandomPropertiesId randomEnchantmentId = 0, GuidSet const& allowedLooters = GuidSet(), ItemContext context = ItemContext::NONE, std::vector<int32> const* bonusListIDs = nullptr, 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/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index ac91cf6a929..809b6ad42a7 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -377,7 +377,7 @@ void Guild::BankTab::LoadFromDB(Field* fields) bool Guild::BankTab::LoadItemFromDB(Field* fields) { - uint8 slotId = fields[55].GetUInt8(); + uint8 slotId = fields[56].GetUInt8(); ObjectGuid::LowType itemGuid = fields[0].GetUInt64(); uint32 itemEntry = fields[1].GetUInt32(); if (slotId >= GUILD_BANK_MAX_SLOTS) @@ -2640,7 +2640,7 @@ void Guild::LoadBankTabFromDB(Field* fields) bool Guild::LoadBankItemFromDB(Field* fields) { - uint8 tabId = fields[54].GetUInt8(); + uint8 tabId = fields[55].GetUInt8(); if (tabId >= _GetPurchasedTabsSize()) { TC_LOG_ERROR("guild", "Invalid tab for item (GUID: {}, id: #{}) in guild bank, skipped.", diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp index a086a9f7c45..ba6ba74ac65 100644 --- a/src/server/game/Guilds/GuildMgr.cpp +++ b/src/server/game/Guilds/GuildMgr.cpp @@ -403,21 +403,21 @@ void GuildMgr::LoadGuilds() // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomBonusListId, durability, playedTime, createTime, text, - // 14 15 16 17 18 19 - // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, - // 20 21 22 23 24 25 + // 14 15 16 17 18 19 20 + // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, context, bonusListIDs, randomPropertiesId, + // 21 22 23 24 25 26 // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, itemModifiedAppearanceSpec5, - // 26 27 28 29 30 31 + // 27 28 29 30 31 32 // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, spellItemEnchantmentSpec5, - // 32 33 34 + // 33 34 35 // secondaryItemModifiedAppearanceAllSpecs, secondaryItemModifiedAppearanceSpec1, secondaryItemModifiedAppearanceSpec2, - // 35 36 37 + // 36 37 38 // secondaryItemModifiedAppearanceSpec3, secondaryItemModifiedAppearanceSpec4, secondaryItemModifiedAppearanceSpec5, - // 38 39 40 41 42 43 44 45 46 47 48 49 + // 39 40 41 42 43 44 45 46 47 48 49 50 // gemItemId1, gemBonuses1, gemContext1, gemScalingLevel1, gemItemId2, gemBonuses2, gemContext2, gemScalingLevel2, gemItemId3, gemBonuses3, gemContext3, gemScalingLevel3 - // 50 51 52 + // 51 52 53 // fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId - // 53 54 55 + // 54 55 56 // guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_BANK_ITEMS)); @@ -431,7 +431,7 @@ void GuildMgr::LoadGuilds() do { Field* fields = result->Fetch(); - uint64 guildId = fields[53].GetUInt64(); + uint64 guildId = fields[54].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 8b70eaf7571..65bb92b8580 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -470,7 +470,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem } // now move item from loot to target inventory - Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, &item.BonusListIDs); + Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.randomPropertiesId, item.GetAllowedLooters(), item.context, &item.BonusListIDs); aeResult.Add(newitem, item.count, loot->loot_type, loot->GetDungeonEncounterId()); // mark as looted diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp index cafa7070c3e..79c1ca2522c 100644 --- a/src/server/game/Handlers/VoidStorageHandler.cpp +++ b/src/server/game/Handlers/VoidStorageHandler.cpp @@ -187,7 +187,7 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor return; } - Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, itemVS->RandomBonusListId, GuidSet(), itemVS->Context, &itemVS->BonusListIDs); + Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, itemVS->RandomBonusListId, {}, GuidSet(), itemVS->Context, &itemVS->BonusListIDs); 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 37012bc23f7..df0110210c9 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -47,6 +47,11 @@ LootItem::LootItem(LootStoreItem const& li) : itemid(li.itemid), conditions(li.c case LootStoreItem::Type::Item: { randomBonusListId = GenerateItemRandomBonusListId(itemid); + randomPropertiesId = GenerateItemRandomPropertiesId(itemid); + if (randomPropertiesId < 0) + if (int32 propertySeed = static_cast<int32>(GenerateEnchSuffixFactor(itemid))) + randomPropertiesSeed = propertySeed; + type = LootItemType::Item; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); freeforall = proto && proto->HasFlag(ITEM_FLAG_MULTI_DROP); @@ -975,7 +980,7 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool continue; } - if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs)) + if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, lootItem->randomPropertiesId, GuidSet(), lootItem->context, &lootItem->BonusListIDs)) { player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast, GetDungeonEncounterId()); player->ApplyItemLootedSpell(pItem, true); diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index 9e5703aa8c6..dae5899b302 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -178,6 +178,8 @@ struct TC_GAME_API LootItem uint32 itemid = 0; uint32 LootListId = 0; ItemRandomBonusListId randomBonusListId = 0; + ItemRandomPropertiesId randomPropertiesId = 0; + int32 randomPropertiesSeed = 0; std::vector<int32> BonusListIDs; ItemContext context = ItemContext::NONE; ConditionsReference conditions; // additional loot condition diff --git a/src/server/game/Server/Packets/ItemPacketsCommon.cpp b/src/server/game/Server/Packets/ItemPacketsCommon.cpp index 961b387f8fd..5cc7ac1b958 100644 --- a/src/server/game/Server/Packets/ItemPacketsCommon.cpp +++ b/src/server/game/Server/Packets/ItemPacketsCommon.cpp @@ -57,6 +57,9 @@ void ItemInstance::Initialize(::Item const* item) ItemBonus->Context = item->GetContext(); } + RandomPropertiesID = item->m_itemData->RandomPropertiesID; + RandomPropertiesSeed = item->m_itemData->PropertySeed; + for (UF::ItemMod mod : item->m_itemData->Modifiers->Values) Modifications.Values.emplace_back(mod.Value, ItemModifier(mod.Type)); } @@ -87,6 +90,9 @@ void ItemInstance::Initialize(::LootItem const& lootItem) if (lootItem.randomBonusListId) ItemBonus->BonusListIDs.push_back(lootItem.randomBonusListId); } + + RandomPropertiesID = lootItem.randomPropertiesId; + RandomPropertiesSeed = lootItem.randomPropertiesSeed; } void ItemInstance::Initialize(::VoidStorageItem const* voidItem) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 64519264db8..8e10a21c7bf 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1370,7 +1370,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, GenerateItemRandomBonusListId(newitemid), GuidSet(), context, bonusListIDs); + Item* pItem = player->StoreNewItem(dest, newitemid, true, GenerateItemRandomBonusListId(newitemid), GenerateItemRandomPropertiesId(newitemid), GuidSet(), context, bonusListIDs); // was it successful? return error if not if (!pItem) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index dafe66aa451..babecd2bc40 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1931,6 +1931,9 @@ bool World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Random item bonus list definitions..."); LoadItemRandomBonusListTemplates(); + TC_LOG_INFO("server.loading", "Loading Random item enchantment definitions..."); + LoadItemRandomEnchantmentsTemplates(); + TC_LOG_INFO("server.loading", "Loading Disables"); // must be before loading quests and items DisableMgr::LoadDisables(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index c9dda180791..e661af2dbeb 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1254,7 +1254,7 @@ public: return false; } - Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), itemContext, + Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GenerateItemRandomPropertiesId(itemId), GuidSet(), itemContext, bonusListIDs.empty() ? nullptr : &bonusListIDs); // remove binding (let GM give it to another player later) @@ -1348,7 +1348,7 @@ public: bonusListIDsForItem.insert(bonusListIDsForItem.begin(), contextBonuses.begin(), contextBonuses.end()); } - Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, {}, GuidSet(), itemContext, + Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, {}, {}, GuidSet(), itemContext, bonusListIDsForItem.empty() ? nullptr : &bonusListIDsForItem); if (!item) continue; diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index 861fbc00a43..c37baad08ae 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -118,7 +118,7 @@ public: ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 39883, 1); // Cracked Egg if (msg == EQUIP_ERR_OK) - player->StoreNewItem(dest, 39883, true, GenerateItemRandomBonusListId(39883)); + player->StoreNewItem(dest, 39883, true, GenerateItemRandomBonusListId(39883), GenerateItemRandomPropertiesId(39883)); return true; } @@ -138,7 +138,7 @@ public: ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 44718, 1); // Ripe Disgusting Jar if (msg == EQUIP_ERR_OK) - player->StoreNewItem(dest, 44718, true, GenerateItemRandomBonusListId(44718)); + player->StoreNewItem(dest, 44718, true, GenerateItemRandomBonusListId(44718), GenerateItemRandomPropertiesId(44718)); return true; } |