diff options
author | Shauren <shauren.trinity@gmail.com> | 2023-06-17 16:29:59 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2023-06-17 16:29:59 +0200 |
commit | 0fb8765a6638fd947b59fce44d5c31251d0cdadd (patch) | |
tree | 0414c93f15f760f755b559edb654be3c9865eb1d /src | |
parent | a97cdfc8f5dedc4be1998f0b1667b519fb1ce33b (diff) |
Core/Items: Item bonus generation improvements
* Pass ItemContext to item creation wherever possible
* Support scaling item levels with m+ keystone levels (not used currently)
* Fixed item link validation when client sends it as default uninitialized bonus list with context only
* Support scaling items depending on current active season (seasons not implemented)
* Implemented content tuning redirection
Diffstat (limited to 'src')
36 files changed, 1049 insertions, 380 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index f87b13cb55d..7d75a35aaac 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -293,6 +293,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_CFG_REGIONS, "SELECT MAX(ID) + 1 FROM cfg_regions", CONNECTION_SYNCH); + // ChallengeModeItemBonusOverride.db2 + PrepareStatement(HOTFIX_SEL_CHALLENGE_MODE_ITEM_BONUS_OVERRIDE, "SELECT ID, ItemBonusTreeGroupID, DstItemBonusTreeID, Type, Value, " + "MythicPlusSeasonID, PvPSeasonID, SrcItemBonusTreeID FROM challenge_mode_item_bonus_override WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_CHALLENGE_MODE_ITEM_BONUS_OVERRIDE, "SELECT MAX(ID) + 1 FROM challenge_mode_item_bonus_override", CONNECTION_SYNCH); + // CharTitles.db2 PrepareStatement(HOTFIX_SEL_CHAR_TITLES, "SELECT ID, Name, Name1, MaskID, Flags FROM char_titles WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_CHAR_TITLES, "SELECT MAX(ID) + 1 FROM char_titles", CONNECTION_SYNCH); @@ -427,6 +432,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() " FROM cinematic_sequences WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_CINEMATIC_SEQUENCES, "SELECT MAX(ID) + 1 FROM cinematic_sequences", CONNECTION_SYNCH); + // ConditionalContentTuning.db2 + PrepareStatement(HOTFIX_SEL_CONDITIONAL_CONTENT_TUNING, "SELECT ID, OrderIndex, RedirectContentTuningID, RedirectFlag, ParentContentTuningID" + " FROM conditional_content_tuning WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_CONDITIONAL_CONTENT_TUNING, "SELECT MAX(ID) + 1 FROM conditional_content_tuning", CONNECTION_SYNCH); + // ContentTuning.db2 PrepareStatement(HOTFIX_SEL_CONTENT_TUNING, "SELECT ID, Flags, ExpansionID, MinLevel, MaxLevel, MinLevelType, MaxLevelType, TargetLevelDelta, " "TargetLevelMaxDelta, TargetLevelMin, TargetLevelMax, MinItemLevel, QuestXpMultiplier FROM content_tuning WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); @@ -437,6 +447,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() "ContentTuningID FROM content_tuning_x_expected WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_CONTENT_TUNING_X_EXPECTED, "SELECT MAX(ID) + 1 FROM content_tuning_x_expected", CONNECTION_SYNCH); + // ContentTuningXLabel.db2 + PrepareStatement(HOTFIX_SEL_CONTENT_TUNING_X_LABEL, "SELECT ID, LabelID, ContentTuningID FROM content_tuning_x_label" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_CONTENT_TUNING_X_LABEL, "SELECT MAX(ID) + 1 FROM content_tuning_x_label", CONNECTION_SYNCH); + // ConversationLine.db2 PrepareStatement(HOTFIX_SEL_CONVERSATION_LINE, "SELECT ID, BroadcastTextID, SpellVisualKitID, AdditionalDuration, NextConversationLineID, " "AnimKitID, SpeechType, StartAnimation, EndAnimation FROM conversation_line WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); @@ -821,14 +836,23 @@ void HotfixDatabaseConnection::DoPrepareStatements() " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_BONUS, "SELECT MAX(ID) + 1 FROM item_bonus", CONNECTION_SYNCH); + // ItemBonusListGroupEntry.db2 + PrepareStatement(HOTFIX_SEL_ITEM_BONUS_LIST_GROUP_ENTRY, "SELECT ID, ItemBonusListGroupID, ItemBonusListID, ItemLevelSelectorID, SequenceValue, " + "ItemExtendedCostID, PlayerConditionID, Flags, ItemLogicalCostGroupID FROM item_bonus_list_group_entry WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_BONUS_LIST_GROUP_ENTRY, "SELECT MAX(ID) + 1 FROM item_bonus_list_group_entry", CONNECTION_SYNCH); + // ItemBonusListLevelDelta.db2 PrepareStatement(HOTFIX_SEL_ITEM_BONUS_LIST_LEVEL_DELTA, "SELECT ItemLevelDelta, ID FROM item_bonus_list_level_delta" " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_BONUS_LIST_LEVEL_DELTA, "SELECT MAX(ID) + 1 FROM item_bonus_list_level_delta", CONNECTION_SYNCH); + // ItemBonusTree.db2 + PrepareStatement(HOTFIX_SEL_ITEM_BONUS_TREE, "SELECT ID, Flags, InventoryTypeSlotMask FROM item_bonus_tree WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_BONUS_TREE, "SELECT MAX(ID) + 1 FROM item_bonus_tree", CONNECTION_SYNCH); + // ItemBonusTreeNode.db2 PrepareStatement(HOTFIX_SEL_ITEM_BONUS_TREE_NODE, "SELECT ID, ItemContext, ChildItemBonusTreeID, ChildItemBonusListID, ChildItemLevelSelectorID, " - "ChildItemBonusListGroupID, IblGroupPointsModSetID, Unknown1010_1, Unknown1010_2, ParentItemBonusTreeID FROM item_bonus_tree_node" + "ChildItemBonusListGroupID, IblGroupPointsModSetID, MinMythicPlusLevel, MaxMythicPlusLevel, ParentItemBonusTreeID FROM item_bonus_tree_node" " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_BONUS_TREE_NODE, "SELECT MAX(ID) + 1 FROM item_bonus_tree_node", CONNECTION_SYNCH); @@ -842,6 +866,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_CLASS, "SELECT MAX(ID) + 1 FROM item_class", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_CLASS, "SELECT ID, ClassName_lang FROM item_class_locale WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH); + // ItemContextPickerEntry.db2 + PrepareStatement(HOTFIX_SEL_ITEM_CONTEXT_PICKER_ENTRY, "SELECT ID, ItemCreationContext, OrderIndex, PVal, LabelID, Flags, PlayerConditionID, " + "ItemContextPickerID FROM item_context_picker_entry WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_CONTEXT_PICKER_ENTRY, "SELECT MAX(ID) + 1 FROM item_context_picker_entry", CONNECTION_SYNCH); + // ItemCurrencyCost.db2 PrepareStatement(HOTFIX_SEL_ITEM_CURRENCY_COST, "SELECT ID, ItemID FROM item_currency_cost WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_CURRENCY_COST, "SELECT MAX(ID) + 1 FROM item_currency_cost", CONNECTION_SYNCH); @@ -1140,6 +1169,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_MOVIE, "SELECT MAX(ID) + 1 FROM movie", CONNECTION_SYNCH); + // MythicPlusSeason.db2 + PrepareStatement(HOTFIX_SEL_MYTHIC_PLUS_SEASON, "SELECT ID, MilestoneSeason, ExpansionLevel, HeroicLFGDungeonMinGear FROM mythic_plus_season" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_MYTHIC_PLUS_SEASON, "SELECT MAX(ID) + 1 FROM mythic_plus_season", CONNECTION_SYNCH); + // NameGen.db2 PrepareStatement(HOTFIX_SEL_NAME_GEN, "SELECT ID, Name, RaceID, Sex FROM name_gen WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_NAME_GEN, "SELECT MAX(ID) + 1 FROM name_gen", CONNECTION_SYNCH); @@ -1228,6 +1262,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_PVP_ITEM, "SELECT ID, ItemID, ItemLevelDelta FROM pvp_item WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_PVP_ITEM, "SELECT MAX(ID) + 1 FROM pvp_item", CONNECTION_SYNCH); + // PvpSeason.db2 + PrepareStatement(HOTFIX_SEL_PVP_SEASON, "SELECT ID, MilestoneSeason, AllianceAchievementID, HordeAchievementID FROM pvp_season" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PVP_SEASON, "SELECT MAX(ID) + 1 FROM pvp_season", CONNECTION_SYNCH); + // PvpTalent.db2 PrepareStatement(HOTFIX_SEL_PVP_TALENT, "SELECT Description, ID, SpecID, SpellID, OverridesSpellID, Flags, ActionBarSpellID, PvpTalentCategoryID, " "LevelRequired, PlayerConditionID FROM pvp_talent WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index f43b85fae5a..d2219522a1f 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -185,6 +185,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_CFG_REGIONS, HOTFIX_SEL_CFG_REGIONS_MAX_ID, + HOTFIX_SEL_CHALLENGE_MODE_ITEM_BONUS_OVERRIDE, + HOTFIX_SEL_CHALLENGE_MODE_ITEM_BONUS_OVERRIDE_MAX_ID, + HOTFIX_SEL_CHAR_TITLES, HOTFIX_SEL_CHAR_TITLES_MAX_ID, HOTFIX_SEL_CHAR_TITLES_LOCALE, @@ -250,12 +253,18 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_CINEMATIC_SEQUENCES, HOTFIX_SEL_CINEMATIC_SEQUENCES_MAX_ID, + HOTFIX_SEL_CONDITIONAL_CONTENT_TUNING, + HOTFIX_SEL_CONDITIONAL_CONTENT_TUNING_MAX_ID, + HOTFIX_SEL_CONTENT_TUNING, HOTFIX_SEL_CONTENT_TUNING_MAX_ID, HOTFIX_SEL_CONTENT_TUNING_X_EXPECTED, HOTFIX_SEL_CONTENT_TUNING_X_EXPECTED_MAX_ID, + HOTFIX_SEL_CONTENT_TUNING_X_LABEL, + HOTFIX_SEL_CONTENT_TUNING_X_LABEL_MAX_ID, + HOTFIX_SEL_CONVERSATION_LINE, HOTFIX_SEL_CONVERSATION_LINE_MAX_ID, @@ -473,9 +482,15 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_ITEM_BONUS, HOTFIX_SEL_ITEM_BONUS_MAX_ID, + HOTFIX_SEL_ITEM_BONUS_LIST_GROUP_ENTRY, + HOTFIX_SEL_ITEM_BONUS_LIST_GROUP_ENTRY_MAX_ID, + HOTFIX_SEL_ITEM_BONUS_LIST_LEVEL_DELTA, HOTFIX_SEL_ITEM_BONUS_LIST_LEVEL_DELTA_MAX_ID, + HOTFIX_SEL_ITEM_BONUS_TREE, + HOTFIX_SEL_ITEM_BONUS_TREE_MAX_ID, + HOTFIX_SEL_ITEM_BONUS_TREE_NODE, HOTFIX_SEL_ITEM_BONUS_TREE_NODE_MAX_ID, @@ -486,6 +501,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_ITEM_CLASS_MAX_ID, HOTFIX_SEL_ITEM_CLASS_LOCALE, + HOTFIX_SEL_ITEM_CONTEXT_PICKER_ENTRY, + HOTFIX_SEL_ITEM_CONTEXT_PICKER_ENTRY_MAX_ID, + HOTFIX_SEL_ITEM_CURRENCY_COST, HOTFIX_SEL_ITEM_CURRENCY_COST_MAX_ID, @@ -654,6 +672,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_MOVIE, HOTFIX_SEL_MOVIE_MAX_ID, + HOTFIX_SEL_MYTHIC_PLUS_SEASON, + HOTFIX_SEL_MYTHIC_PLUS_SEASON_MAX_ID, + HOTFIX_SEL_NAME_GEN, HOTFIX_SEL_NAME_GEN_MAX_ID, @@ -701,6 +722,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_PVP_ITEM, HOTFIX_SEL_PVP_ITEM_MAX_ID, + HOTFIX_SEL_PVP_SEASON, + HOTFIX_SEL_PVP_SEASON_MAX_ID, + HOTFIX_SEL_PVP_TALENT, HOTFIX_SEL_PVP_TALENT_MAX_ID, HOTFIX_SEL_PVP_TALENT_LOCALE, diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index 202da5744a7..4eda890f135 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -32,6 +32,7 @@ #include "Group.h" #include "InstanceScript.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "LanguageMgr.h" #include "Log.h" #include "Map.h" @@ -3089,7 +3090,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::PlayerHasItemWithBonusListFromTreeAndQuality: // 222 { - std::set<uint32> bonusListIDs = sDB2Manager.GetAllItemBonusTreeBonuses(reqValue); + std::vector<int32> bonusListIDs = ItemBonusMgr::GetAllBonusListsForTree(reqValue); if (bonusListIDs.empty()) return false; @@ -3097,7 +3098,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 { bool hasBonus = std::any_of(item->GetBonusListIDs().begin(), item->GetBonusListIDs().end(), [&bonusListIDs](int32 bonusListID) { - return bonusListIDs.find(bonusListID) != bonusListIDs.end(); + return std::find(bonusListIDs.begin(), bonusListIDs.end(), bonusListID) != bonusListIDs.end(); }); return hasBonus ? ItemSearchCallbackResult::Stop : ItemSearchCallbackResult::Continue; }); diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp index e12a519f67d..64ce77b3f2d 100644 --- a/src/server/game/Chat/HyperlinkTags.cpp +++ b/src/server/game/Chat/HyperlinkTags.cpp @@ -18,6 +18,7 @@ #include "Hyperlinks.h" #include "DB2Stores.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "ObjectMgr.h" #include "SpellInfo.h" #include "SpellMgr.h" @@ -292,12 +293,22 @@ bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, std::string val.ItemBonusListIDs.resize(numBonusListIDs); for (int32& itemBonusListID : val.ItemBonusListIDs) { - if (!t.TryConsumeTo(itemBonusListID) || !sDB2Manager.GetItemBonusList(itemBonusListID)) + if (!t.TryConsumeTo(itemBonusListID)) return false; evaluatedBonus.AddBonusList(itemBonusListID); } + if (!val.ItemBonusListIDs.empty() && val.ItemBonusListIDs[0] == 3524) // default uninitialized bonus + { + val.ItemBonusListIDs = ItemBonusMgr::GetBonusListsForItem(itemId, ItemContext(val.Context)); + + // reset bonuses + evaluatedBonus.Initialize(val.Item); + for (int32 itemBonusListID : val.ItemBonusListIDs) + evaluatedBonus.AddBonusList(itemBonusListID); + } + val.Quality = evaluatedBonus.Quality; val.Suffix = sItemNameDescriptionStore.LookupEntry(evaluatedBonus.Suffix); if (evaluatedBonus.Suffix && !val.Suffix) @@ -322,7 +333,7 @@ bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, std::string val.GemItemBonusListIDs[i].resize(numBonusListIDs); for (int32& itemBonusListID : val.GemItemBonusListIDs[i]) - if (!t.TryConsumeTo(itemBonusListID) || !sDB2Manager.GetItemBonusList(itemBonusListID)) + if (!t.TryConsumeTo(itemBonusListID)) return false; } diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 96acddf6a11..ed541a1116d 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -808,6 +808,23 @@ struct CfgRegionsLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 6, &Cfg_RegionsMeta::Instance, HOTFIX_SEL_CFG_REGIONS }; }; +struct ChallengeModeItemBonusOverrideLoadInfo +{ + static constexpr DB2FieldMeta Fields[8] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "ItemBonusTreeGroupID" }, + { true, FT_INT, "DstItemBonusTreeID" }, + { true, FT_BYTE, "Type" }, + { true, FT_INT, "Value" }, + { true, FT_INT, "MythicPlusSeasonID" }, + { true, FT_INT, "PvPSeasonID" }, + { false, FT_INT, "SrcItemBonusTreeID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 8, &ChallengeModeItemBonusOverrideMeta::Instance, HOTFIX_SEL_CHALLENGE_MODE_ITEM_BONUS_OVERRIDE }; +}; + struct CharTitlesLoadInfo { static constexpr DB2FieldMeta Fields[5] = @@ -1218,6 +1235,20 @@ struct CinematicSequencesLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 10, &CinematicSequencesMeta::Instance, HOTFIX_SEL_CINEMATIC_SEQUENCES }; }; +struct ConditionalContentTuningLoadInfo +{ + static constexpr DB2FieldMeta Fields[5] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "OrderIndex" }, + { true, FT_INT, "RedirectContentTuningID" }, + { true, FT_INT, "RedirectFlag" }, + { false, FT_INT, "ParentContentTuningID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 5, &ConditionalContentTuningMeta::Instance, HOTFIX_SEL_CONDITIONAL_CONTENT_TUNING }; +}; + struct ContentTuningLoadInfo { static constexpr DB2FieldMeta Fields[13] = @@ -1254,6 +1285,18 @@ struct ContentTuningXExpectedLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 5, &ContentTuningXExpectedMeta::Instance, HOTFIX_SEL_CONTENT_TUNING_X_EXPECTED }; }; +struct ContentTuningXLabelLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "LabelID" }, + { false, FT_INT, "ContentTuningID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &ContentTuningXLabelMeta::Instance, HOTFIX_SEL_CONTENT_TUNING_X_LABEL }; +}; + struct ConversationLineLoadInfo { static constexpr DB2FieldMeta Fields[9] = @@ -2587,6 +2630,24 @@ struct ItemBonusLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 8, &ItemBonusMeta::Instance, HOTFIX_SEL_ITEM_BONUS }; }; +struct ItemBonusListGroupEntryLoadInfo +{ + static constexpr DB2FieldMeta Fields[9] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "ItemBonusListGroupID" }, + { true, FT_INT, "ItemBonusListID" }, + { true, FT_INT, "ItemLevelSelectorID" }, + { true, FT_INT, "SequenceValue" }, + { true, FT_INT, "ItemExtendedCostID" }, + { true, FT_INT, "PlayerConditionID" }, + { true, FT_INT, "Flags" }, + { true, FT_INT, "ItemLogicalCostGroupID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 9, &ItemBonusListGroupEntryMeta::Instance, HOTFIX_SEL_ITEM_BONUS_LIST_GROUP_ENTRY }; +}; + struct ItemBonusListLevelDeltaLoadInfo { static constexpr DB2FieldMeta Fields[2] = @@ -2598,6 +2659,18 @@ struct ItemBonusListLevelDeltaLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 2, &ItemBonusListLevelDeltaMeta::Instance, HOTFIX_SEL_ITEM_BONUS_LIST_LEVEL_DELTA }; }; +struct ItemBonusTreeLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "Flags" }, + { true, FT_INT, "InventoryTypeSlotMask" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &ItemBonusTreeMeta::Instance, HOTFIX_SEL_ITEM_BONUS_TREE }; +}; + struct ItemBonusTreeNodeLoadInfo { static constexpr DB2FieldMeta Fields[10] = @@ -2609,8 +2682,8 @@ struct ItemBonusTreeNodeLoadInfo { false, FT_SHORT, "ChildItemLevelSelectorID" }, { true, FT_INT, "ChildItemBonusListGroupID" }, { true, FT_INT, "IblGroupPointsModSetID" }, - { true, FT_INT, "Unknown1010_1" }, - { true, FT_INT, "Unknown1010_2" }, + { true, FT_INT, "MinMythicPlusLevel" }, + { true, FT_INT, "MaxMythicPlusLevel" }, { false, FT_INT, "ParentItemBonusTreeID" }, }; @@ -2644,6 +2717,23 @@ struct ItemClassLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 5, &ItemClassMeta::Instance, HOTFIX_SEL_ITEM_CLASS }; }; +struct ItemContextPickerEntryLoadInfo +{ + static constexpr DB2FieldMeta Fields[8] = + { + { false, FT_INT, "ID" }, + { false, FT_BYTE, "ItemCreationContext" }, + { false, FT_BYTE, "OrderIndex" }, + { true, FT_INT, "PVal" }, + { true, FT_INT, "LabelID" }, + { false, FT_INT, "Flags" }, + { false, FT_INT, "PlayerConditionID" }, + { false, FT_INT, "ItemContextPickerID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 8, &ItemContextPickerEntryMeta::Instance, HOTFIX_SEL_ITEM_CONTEXT_PICKER_ENTRY }; +}; + struct ItemCurrencyCostLoadInfo { static constexpr DB2FieldMeta Fields[2] = @@ -3708,6 +3798,19 @@ struct MovieLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 6, &MovieMeta::Instance, HOTFIX_SEL_MOVIE }; }; +struct MythicPlusSeasonLoadInfo +{ + static constexpr DB2FieldMeta Fields[4] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "MilestoneSeason" }, + { true, FT_INT, "ExpansionLevel" }, + { true, FT_INT, "HeroicLFGDungeonMinGear" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 4, &MythicPlusSeasonMeta::Instance, HOTFIX_SEL_MYTHIC_PLUS_SEASON }; +}; + struct NameGenLoadInfo { static constexpr DB2FieldMeta Fields[4] = @@ -4074,6 +4177,19 @@ struct PvpItemLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 3, &PVPItemMeta::Instance, HOTFIX_SEL_PVP_ITEM }; }; +struct PvpSeasonLoadInfo +{ + static constexpr DB2FieldMeta Fields[4] = + { + { false, FT_INT, "ID" }, + { true, FT_INT, "MilestoneSeason" }, + { true, FT_INT, "AllianceAchievementID" }, + { true, FT_INT, "HordeAchievementID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 4, &PvpSeasonMeta::Instance, HOTFIX_SEL_PVP_SEASON }; +}; + struct PvpTalentLoadInfo { static constexpr DB2FieldMeta Fields[10] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 05c093d7717..f5c8878f620 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -86,6 +86,7 @@ DB2Storage<BattlemasterListEntry> sBattlemasterListStore("Battlema DB2Storage<BroadcastTextEntry> sBroadcastTextStore("BroadcastText.db2", &BroadcastTextLoadInfo::Instance); DB2Storage<BroadcastTextDurationEntry> sBroadcastTextDurationStore("BroadcastTextDuration.db2", &BroadcastTextDurationLoadInfo::Instance); DB2Storage<Cfg_RegionsEntry> sCfgRegionsStore("Cfg_Regions.db2", &CfgRegionsLoadInfo::Instance); +DB2Storage<ChallengeModeItemBonusOverrideEntry> sChallengeModeItemBonusOverrideStore("ChallengeModeItemBonusOverride.db2", &ChallengeModeItemBonusOverrideLoadInfo::Instance); DB2Storage<CharTitlesEntry> sCharTitlesStore("CharTitles.db2", &CharTitlesLoadInfo::Instance); DB2Storage<CharacterLoadoutEntry> sCharacterLoadoutStore("CharacterLoadout.db2", &CharacterLoadoutLoadInfo::Instance); DB2Storage<CharacterLoadoutItemEntry> sCharacterLoadoutItemStore("CharacterLoadoutItem.db2", &CharacterLoadoutItemLoadInfo::Instance); @@ -105,8 +106,10 @@ DB2Storage<ChrRacesEntry> sChrRacesStore("ChrRaces.db2", & DB2Storage<ChrSpecializationEntry> sChrSpecializationStore("ChrSpecialization.db2", &ChrSpecializationLoadInfo::Instance); DB2Storage<CinematicCameraEntry> sCinematicCameraStore("CinematicCamera.db2", &CinematicCameraLoadInfo::Instance); DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore("CinematicSequences.db2", &CinematicSequencesLoadInfo::Instance); +DB2Storage<ConditionalContentTuningEntry> sConditionalContentTuningStore("ConditionalContentTuning.db2", &ConditionalContentTuningLoadInfo::Instance); DB2Storage<ContentTuningEntry> sContentTuningStore("ContentTuning.db2", &ContentTuningLoadInfo::Instance); DB2Storage<ContentTuningXExpectedEntry> sContentTuningXExpectedStore("ContentTuningXExpected.db2", &ContentTuningXExpectedLoadInfo::Instance); +DB2Storage<ContentTuningXLabelEntry> sContentTuningXLabelStore("ContentTuningXLabel.db2", &ContentTuningXLabelLoadInfo::Instance); DB2Storage<ConversationLineEntry> sConversationLineStore("ConversationLine.db2", &ConversationLineLoadInfo::Instance); DB2Storage<CorruptionEffectsEntry> sCorruptionEffectsStore("CorruptionEffects.db2", &CorruptionEffectsLoadInfo::Instance); DB2Storage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", &CreatureDisplayInfoLoadInfo::Instance); @@ -172,10 +175,13 @@ DB2Storage<ItemArmorShieldEntry> sItemArmorShieldStore("ItemArmor DB2Storage<ItemArmorTotalEntry> sItemArmorTotalStore("ItemArmorTotal.db2", &ItemArmorTotalLoadInfo::Instance); DB2Storage<ItemBagFamilyEntry> sItemBagFamilyStore("ItemBagFamily.db2", &ItemBagFamilyLoadInfo::Instance); DB2Storage<ItemBonusEntry> sItemBonusStore("ItemBonus.db2", &ItemBonusLoadInfo::Instance); +DB2Storage<ItemBonusListGroupEntryEntry> sItemBonusListGroupEntryStore("ItemBonusListGroupEntry.db2", &ItemBonusListGroupEntryLoadInfo::Instance); DB2Storage<ItemBonusListLevelDeltaEntry> sItemBonusListLevelDeltaStore("ItemBonusListLevelDelta.db2", &ItemBonusListLevelDeltaLoadInfo::Instance); +DB2Storage<ItemBonusTreeEntry> sItemBonusTreeStore("ItemBonusTree.db2", &ItemBonusTreeLoadInfo::Instance); DB2Storage<ItemBonusTreeNodeEntry> sItemBonusTreeNodeStore("ItemBonusTreeNode.db2", &ItemBonusTreeNodeLoadInfo::Instance); DB2Storage<ItemChildEquipmentEntry> sItemChildEquipmentStore("ItemChildEquipment.db2", &ItemChildEquipmentLoadInfo::Instance); DB2Storage<ItemClassEntry> sItemClassStore("ItemClass.db2", &ItemClassLoadInfo::Instance); +DB2Storage<ItemContextPickerEntryEntry> sItemContextPickerEntryStore("ItemContextPickerEntry.db2", &ItemContextPickerEntryLoadInfo::Instance); DB2Storage<ItemCurrencyCostEntry> sItemCurrencyCostStore("ItemCurrencyCost.db2", &ItemCurrencyCostLoadInfo::Instance); DB2Storage<ItemDamageAmmoEntry> sItemDamageAmmoStore("ItemDamageAmmo.db2", &ItemDamageAmmoLoadInfo::Instance); DB2Storage<ItemDamageOneHandEntry> sItemDamageOneHandStore("ItemDamageOneHand.db2", &ItemDamageOneHandLoadInfo::Instance); @@ -227,6 +233,7 @@ DB2Storage<MountEntry> sMountStore("Mount.db2", &MountL DB2Storage<MountTypeXCapabilityEntry> sMountTypeXCapabilityStore("MountTypeXCapability.db2", &MountTypeXCapabilityLoadInfo::Instance); DB2Storage<MountXDisplayEntry> sMountXDisplayStore("MountXDisplay.db2", &MountXDisplayLoadInfo::Instance); DB2Storage<MovieEntry> sMovieStore("Movie.db2", &MovieLoadInfo::Instance); +DB2Storage<MythicPlusSeasonEntry> sMythicPlusSeasonStore("MythicPlusSeason.db2", &MythicPlusSeasonLoadInfo::Instance); DB2Storage<NameGenEntry> sNameGenStore("NameGen.db2", &NameGenLoadInfo::Instance); DB2Storage<NamesProfanityEntry> sNamesProfanityStore("NamesProfanity.db2", &NamesProfanityLoadInfo::Instance); DB2Storage<NamesReservedEntry> sNamesReservedStore("NamesReserved.db2", &NamesReservedLoadInfo::Instance); @@ -242,6 +249,7 @@ DB2Storage<PowerTypeEntry> sPowerTypeStore("PowerType.db2", DB2Storage<PrestigeLevelInfoEntry> sPrestigeLevelInfoStore("PrestigeLevelInfo.db2", &PrestigeLevelInfoLoadInfo::Instance); DB2Storage<PVPDifficultyEntry> sPVPDifficultyStore("PVPDifficulty.db2", &PvpDifficultyLoadInfo::Instance); DB2Storage<PVPItemEntry> sPVPItemStore("PVPItem.db2", &PvpItemLoadInfo::Instance); +DB2Storage<PvpSeasonEntry> sPvpSeasonStore("PvpSeason.db2", &PvpSeasonLoadInfo::Instance); DB2Storage<PvpTalentEntry> sPvpTalentStore("PvpTalent.db2", &PvpTalentLoadInfo::Instance); DB2Storage<PvpTalentCategoryEntry> sPvpTalentCategoryStore("PvpTalentCategory.db2", &PvpTalentCategoryLoadInfo::Instance); DB2Storage<PvpTalentSlotUnlockEntry> sPvpTalentSlotUnlockStore("PvpTalentSlotUnlock.db2", &PvpTalentSlotUnlockLoadInfo::Instance); @@ -371,15 +379,6 @@ TaxiPathNodesByPath sTaxiPathNodesByPath; DEFINE_DB2_SET_COMPARATOR(ChrClassesXPowerTypesEntry) -struct ItemLevelSelectorQualityEntryComparator -{ - bool operator()(ItemLevelSelectorQualityEntry const* left, ItemLevelSelectorQualityEntry const* right) const { return Compare(left, right); } - bool operator()(ItemLevelSelectorQualityEntry const* left, ItemQualities quality) const { return left->Quality < quality; } - static bool Compare(ItemLevelSelectorQualityEntry const* left, ItemLevelSelectorQualityEntry const* right); -}; - -void LoadAzeriteEmpoweredItemUnlockMappings(std::unordered_map<int32, std::vector<AzeriteUnlockMappingEntry const*>> const& azeriteUnlockMappingsBySet, uint32 itemId); - typedef std::map<uint32 /*hash*/, DB2StorageBase*> StorageMap; typedef std::unordered_map<uint32 /*areaGroupId*/, std::vector<uint32/*areaId*/>> AreaGroupMemberContainer; typedef std::unordered_map<uint32, std::vector<ArtifactPowerEntry const*>> ArtifactPowersContainer; @@ -392,15 +391,10 @@ typedef std::unordered_map<uint32, std::vector<uint32>> FactionTeamContainer; typedef std::unordered_map<uint32, HeirloomEntry const*> HeirloomItemsContainer; typedef std::unordered_map<uint32 /*glyphPropertiesId*/, std::vector<uint32>> GlyphBindableSpellsContainer; typedef std::unordered_map<uint32 /*glyphPropertiesId*/, std::vector<uint32>> GlyphRequiredSpecsContainer; -typedef std::unordered_map<uint32 /*bonusListId*/, DB2Manager::ItemBonusList> ItemBonusListContainer; -typedef std::unordered_map<int16, uint32> ItemBonusListLevelDeltaContainer; -typedef std::unordered_multimap<uint32 /*itemId*/, uint32 /*bonusTreeId*/> ItemToBonusTreeContainer; typedef std::unordered_map<uint32 /*itemId*/, ItemChildEquipmentEntry const*> ItemChildEquipmentContainer; typedef std::array<ItemClassEntry const*, 20> ItemClassByOldEnumContainer; typedef std::unordered_map<uint32, std::vector<ItemLimitCategoryConditionEntry const*>> ItemLimitCategoryConditionContainer; -typedef std::set<ItemLevelSelectorQualityEntry const*, ItemLevelSelectorQualityEntryComparator> ItemLevelSelectorQualities; typedef std::unordered_map<uint32 /*itemId | appearanceMod << 24*/, ItemModifiedAppearanceEntry const*> ItemModifiedAppearanceByItemContainer; -typedef std::unordered_map<uint32, std::set<ItemBonusTreeNodeEntry const*>> ItemBonusTreeContainer; typedef std::unordered_map<uint32, std::vector<ItemSetSpellEntry const*>> ItemSetSpellContainer; typedef std::unordered_map<uint32, std::vector<ItemSpecOverrideEntry const*>> ItemSpecOverridesContainer; typedef std::unordered_map<uint32, std::unordered_map<uint32, MapDifficultyEntry const*>> MapDifficultyContainer; @@ -450,7 +444,6 @@ namespace std::array<AzeriteItemMilestonePowerEntry const*, MAX_AZERITE_ESSENCE_SLOT> _azeriteItemMilestonePowerByEssenceSlot; std::unordered_map<uint32 /*azeritePowerSetId*/, std::vector<AzeritePowerSetMemberEntry const*>> _azeritePowers; std::unordered_map<std::pair<uint32 /*azeriteUnlockSetId*/, ItemContext>, std::array<uint8, MAX_AZERITE_EMPOWERED_TIER>> _azeriteTierUnlockLevels; - std::unordered_map<std::pair<uint32 /*itemId*/, ItemContext>, AzeriteUnlockMappingEntry const*> _azeriteUnlockMappings; std::unordered_map<std::pair<int32 /*broadcastTextId*/, CascLocaleBit /*cascLocaleBit*/>, int32> _broadcastTextDurations; std::array<ChrClassUIDisplayEntry const*, MAX_CLASSES> _uiDisplayByClass; std::array<std::array<uint32, MAX_POWERS>, MAX_CLASSES> _powersByClass; @@ -460,6 +453,8 @@ namespace std::unordered_map<std::pair<uint8 /*race*/, uint8/*gender*/>, std::vector<ChrCustomizationOptionEntry const*>> _chrCustomizationOptionsByRaceAndGender; std::unordered_map<uint32 /*chrCustomizationReqId*/, std::vector<std::pair<uint32 /*chrCustomizationOptionId*/, std::vector<uint32>>>> _chrCustomizationRequiredChoices; ChrSpecializationByIndexContainer _chrSpecializationsByIndex; + std::unordered_multimap<uint32, ConditionalContentTuningEntry const*> _conditionalContentTuning; + std::unordered_set<std::pair<uint32, int32>> _contentTuningLabels; std::unordered_multimap<uint32, CurrencyContainerEntry const*> _currencyContainers; CurvePointsContainer _curvePoints; EmotesTextSoundContainer _emoteTextSounds; @@ -470,16 +465,11 @@ namespace HeirloomItemsContainer _heirlooms; GlyphBindableSpellsContainer _glyphBindableSpells; GlyphRequiredSpecsContainer _glyphRequiredSpecs; - ItemBonusListContainer _itemBonusLists; - ItemBonusListLevelDeltaContainer _itemLevelDeltaToBonusListContainer; - ItemBonusTreeContainer _itemBonusTrees; ItemChildEquipmentContainer _itemChildEquipment; ItemClassByOldEnumContainer _itemClassByOldEnum; std::unordered_set<uint32> _itemsWithCurrencyCost; ItemLimitCategoryConditionContainer _itemCategoryConditions; - std::unordered_map<uint32 /*itemLevelSelectorQualitySetId*/, ItemLevelSelectorQualities> _itemLevelQualitySelectorQualities; ItemModifiedAppearanceByItemContainer _itemModifiedAppearancesByItem; - ItemToBonusTreeContainer _itemToBonusTree; ItemSetSpellContainer _itemSetSpells; ItemSpecOverridesContainer _itemSpecOverrides; std::vector<JournalTierEntry const*> _journalTiersByIndex; @@ -689,6 +679,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sBroadcastTextStore); LOAD_DB2(sBroadcastTextDurationStore); LOAD_DB2(sCfgRegionsStore); + LOAD_DB2(sChallengeModeItemBonusOverrideStore); LOAD_DB2(sCharTitlesStore); LOAD_DB2(sCharacterLoadoutStore); LOAD_DB2(sCharacterLoadoutItemStore); @@ -708,8 +699,10 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sChrSpecializationStore); LOAD_DB2(sCinematicCameraStore); LOAD_DB2(sCinematicSequencesStore); + LOAD_DB2(sConditionalContentTuningStore); LOAD_DB2(sContentTuningStore); LOAD_DB2(sContentTuningXExpectedStore); + LOAD_DB2(sContentTuningXLabelStore); LOAD_DB2(sConversationLineStore); LOAD_DB2(sCorruptionEffectsStore); LOAD_DB2(sCreatureDisplayInfoStore); @@ -775,10 +768,13 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sItemArmorTotalStore); LOAD_DB2(sItemBagFamilyStore); LOAD_DB2(sItemBonusStore); + LOAD_DB2(sItemBonusListGroupEntryStore); LOAD_DB2(sItemBonusListLevelDeltaStore); + LOAD_DB2(sItemBonusTreeStore); LOAD_DB2(sItemBonusTreeNodeStore); LOAD_DB2(sItemChildEquipmentStore); LOAD_DB2(sItemClassStore); + LOAD_DB2(sItemContextPickerEntryStore); LOAD_DB2(sItemCurrencyCostStore); LOAD_DB2(sItemDamageAmmoStore); LOAD_DB2(sItemDamageOneHandStore); @@ -830,6 +826,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sMountTypeXCapabilityStore); LOAD_DB2(sMountXDisplayStore); LOAD_DB2(sMovieStore); + LOAD_DB2(sMythicPlusSeasonStore); LOAD_DB2(sNameGenStore); LOAD_DB2(sNamesProfanityStore); LOAD_DB2(sNamesReservedStore); @@ -845,6 +842,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sPrestigeLevelInfoStore); LOAD_DB2(sPVPDifficultyStore); LOAD_DB2(sPVPItemStore); + LOAD_DB2(sPvpSeasonStore); LOAD_DB2(sPvpTalentStore); LOAD_DB2(sPvpTalentCategoryStore); LOAD_DB2(sPvpTalentSlotUnlockStore); @@ -1042,10 +1040,6 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul for (AzeriteTierUnlockEntry const* azeriteTierUnlock : sAzeriteTierUnlockStore) _azeriteTierUnlockLevels[std::pair<uint32, ItemContext>{ azeriteTierUnlock->AzeriteTierUnlockSetID, ItemContext(azeriteTierUnlock->ItemCreationContext) }][azeriteTierUnlock->Tier] = azeriteTierUnlock->AzeriteLevel; - std::unordered_map<int32, std::vector<AzeriteUnlockMappingEntry const*>> azeriteUnlockMappings; - for (AzeriteUnlockMappingEntry const* azeriteUnlockMapping : sAzeriteUnlockMappingStore) - azeriteUnlockMappings[azeriteUnlockMapping->AzeriteUnlockMappingSetID].push_back(azeriteUnlockMapping); - for (BattlemasterListEntry const* battlemaster : sBattlemasterListStore) { if (battlemaster->MaxLevel < battlemaster->MinLevel) @@ -1196,10 +1190,16 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul _chrSpecializationsByIndex[storageIndex][chrSpec->OrderIndex] = chrSpec; } + for (ConditionalContentTuningEntry const* conditionalContentTuning : sConditionalContentTuningStore) + _conditionalContentTuning.emplace(conditionalContentTuning->ParentContentTuningID, conditionalContentTuning); + for (ContentTuningXExpectedEntry const* contentTuningXExpectedStat : sContentTuningXExpectedStore) if (sExpectedStatModStore.LookupEntry(contentTuningXExpectedStat->ExpectedStatModID)) _expectedStatModsByContentTuning[contentTuningXExpectedStat->ContentTuningID].push_back(contentTuningXExpectedStat); + for (ContentTuningXLabelEntry const* contentTuningXLabel : sContentTuningXLabelStore) + _contentTuningLabels.emplace(contentTuningXLabel->ContentTuningID, contentTuningXLabel->LabelID); + for (CurrencyContainerEntry const* currencyContainer : sCurrencyContainerStore) _currencyContainers.emplace(currencyContainer->CurrencyTypesID, currencyContainer); @@ -1242,15 +1242,6 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul for (GlyphRequiredSpecEntry const* glyphRequiredSpec : sGlyphRequiredSpecStore) _glyphRequiredSpecs[glyphRequiredSpec->GlyphPropertiesID].push_back(glyphRequiredSpec->ChrSpecializationID); - for (ItemBonusEntry const* bonus : sItemBonusStore) - _itemBonusLists[bonus->ParentItemBonusListID].push_back(bonus); - - for (ItemBonusListLevelDeltaEntry const* itemBonusListLevelDelta : sItemBonusListLevelDeltaStore) - _itemLevelDeltaToBonusListContainer[itemBonusListLevelDelta->ItemLevelDelta] = itemBonusListLevelDelta->ID; - - for (ItemBonusTreeNodeEntry const* bonusTreeNode : sItemBonusTreeNodeStore) - _itemBonusTrees[bonusTreeNode->ParentItemBonusTreeID].insert(bonusTreeNode); - for (ItemChildEquipmentEntry const* itemChildEquipment : sItemChildEquipmentStore) { ASSERT(_itemChildEquipment.find(itemChildEquipment->ParentItemID) == _itemChildEquipment.end(), "Item must have max 1 child item."); @@ -1270,9 +1261,6 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul for (ItemLimitCategoryConditionEntry const* condition : sItemLimitCategoryConditionStore) _itemCategoryConditions[condition->ParentItemLimitCategoryID].push_back(condition); - for (ItemLevelSelectorQualityEntry const* itemLevelSelectorQuality : sItemLevelSelectorQualityStore) - _itemLevelQualitySelectorQualities[itemLevelSelectorQuality->ParentILSQualitySetID].insert(itemLevelSelectorQuality); - for (ItemModifiedAppearanceEntry const* appearanceMod : sItemModifiedAppearanceStore) { ASSERT(appearanceMod->ItemID <= 0xFFFFFF); @@ -1285,12 +1273,6 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul for (ItemSpecOverrideEntry const* itemSpecOverride : sItemSpecOverrideStore) _itemSpecOverrides[itemSpecOverride->ItemID].push_back(itemSpecOverride); - for (ItemXBonusTreeEntry const* itemBonusTreeAssignment : sItemXBonusTreeStore) - _itemToBonusTree.insert({ itemBonusTreeAssignment->ItemID, itemBonusTreeAssignment->ItemBonusTreeID }); - - for (auto&& kvp : _azeriteEmpoweredItems) - LoadAzeriteEmpoweredItemUnlockMappings(azeriteUnlockMappings, kvp.first); - for (JournalTierEntry const* journalTier : sJournalTierStore) _journalTiersByIndex.push_back(journalTier); @@ -2058,9 +2040,18 @@ ChrSpecializationEntry const* DB2Manager::GetDefaultChrSpecializationForClass(ui return GetChrSpecializationByIndex(class_, INITIAL_SPECIALIZATION_INDEX); } -Optional<ContentTuningLevels> DB2Manager::GetContentTuningData(uint32 contentTuningId, uint32 /*replacementConditionMask*/, bool forItem /*= false*/) const +uint32 DB2Manager::GetRedirectedContentTuningId(uint32 contentTuningId, uint32 redirectFlag) const { - ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(contentTuningId); + for (auto [_, conditionalContentTuning] : Trinity::Containers::MapEqualRange(_conditionalContentTuning, contentTuningId)) + if (conditionalContentTuning->RedirectFlag & redirectFlag) + return conditionalContentTuning->RedirectContentTuningID; + + return contentTuningId; +} + +Optional<ContentTuningLevels> DB2Manager::GetContentTuningData(uint32 contentTuningId, uint32 redirectFlag, bool forItem /*= false*/) const +{ + ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(GetRedirectedContentTuningId(contentTuningId, redirectFlag)); if (!contentTuning) return {}; @@ -2105,6 +2096,11 @@ Optional<ContentTuningLevels> DB2Manager::GetContentTuningData(uint32 contentTun return levels; } +bool DB2Manager::HasContentTuningLabel(uint32 contentTuningId, int32 label) const +{ + return _contentTuningLabels.contains({ contentTuningId, label }); +} + char const* DB2Manager::GetCreatureFamilyPetName(uint32 petfamily, LocaleConstant locale) { if (!petfamily) @@ -2304,28 +2300,30 @@ EmotesTextSoundEntry const* DB2Manager::GetTextSoundEmoteFor(uint32 emote, uint8 template<float(ExpectedStatModEntry::*field)> struct ExpectedStatModReducer { - float operator()(float mod, ContentTuningXExpectedEntry const* contentTuningXExpected) + explicit ExpectedStatModReducer(int32 mythicPlusMilestoneSeason) : ActiveMilestoneSeason(mythicPlusMilestoneSeason) { } + + float operator()(float mod, ContentTuningXExpectedEntry const* contentTuningXExpected) const { if (!contentTuningXExpected) return mod; - //if (contentTuningXExpected->MinMythicPlusSeasonID) - // if (MythicPlusSeasonEntry const* mythicPlusSeason = sMythicPlusSeasonStore.LookupEntry(contentTuningXExpected->MinMythicPlusSeasonID)) - // if (MythicPlusSubSeason < mythicPlusSeason->SubSeason) - // return mod; + if (contentTuningXExpected->MinMythicPlusSeasonID) + if (MythicPlusSeasonEntry const* mythicPlusSeason = sMythicPlusSeasonStore.LookupEntry(contentTuningXExpected->MinMythicPlusSeasonID)) + if (ActiveMilestoneSeason < mythicPlusSeason->MilestoneSeason) + return mod; - //if (contentTuningXExpected->MaxMythicPlusSeasonID) - // if (MythicPlusSeasonEntry const* mythicPlusSeason = sMythicPlusSeasonStore.LookupEntry(contentTuningXExpected->MaxMythicPlusSeasonID)) - // if (MythicPlusSubSeason >= mythicPlusSeason->SubSeason) - // return mod; + if (contentTuningXExpected->MaxMythicPlusSeasonID) + if (MythicPlusSeasonEntry const* mythicPlusSeason = sMythicPlusSeasonStore.LookupEntry(contentTuningXExpected->MaxMythicPlusSeasonID)) + if (ActiveMilestoneSeason >= mythicPlusSeason->MilestoneSeason) + return mod; return mod * sExpectedStatModStore.AssertEntry(contentTuningXExpected->ExpectedStatModID)->*field; } - // int32 MythicPlusSubSeason = 0; + int32 ActiveMilestoneSeason = 0; }; -float DB2Manager::EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int32 expansion, uint32 contentTuningId, Classes unitClass) const +float DB2Manager::EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int32 expansion, uint32 contentTuningId, Classes unitClass, int32 mythicPlusMilestoneSeason) const { auto expectedStatItr = _expectedStatsByLevel.find(std::make_pair(level, expansion)); if (expectedStatItr == _expectedStatsByLevel.end()) @@ -2360,56 +2358,64 @@ float DB2Manager::EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int3 case ExpectedStatType::CreatureHealth: value = expectedStatItr->second->CreatureHealth; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::CreatureHealthMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::CreatureHealthMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->CreatureHealthMod; break; case ExpectedStatType::PlayerHealth: value = expectedStatItr->second->PlayerHealth; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::PlayerHealthMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::PlayerHealthMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->PlayerHealthMod; break; case ExpectedStatType::CreatureAutoAttackDps: value = expectedStatItr->second->CreatureAutoAttackDps; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::CreatureAutoAttackDPSMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::CreatureAutoAttackDPSMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->CreatureAutoAttackDPSMod; break; case ExpectedStatType::CreatureArmor: value = expectedStatItr->second->CreatureArmor; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::CreatureArmorMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::CreatureArmorMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->CreatureArmorMod; break; case ExpectedStatType::PlayerMana: value = expectedStatItr->second->PlayerMana; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::PlayerManaMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::PlayerManaMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->PlayerManaMod; break; case ExpectedStatType::PlayerPrimaryStat: value = expectedStatItr->second->PlayerPrimaryStat; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::PlayerPrimaryStatMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::PlayerPrimaryStatMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->PlayerPrimaryStatMod; break; case ExpectedStatType::PlayerSecondaryStat: value = expectedStatItr->second->PlayerSecondaryStat; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::PlayerSecondaryStatMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::PlayerSecondaryStatMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->PlayerSecondaryStatMod; break; case ExpectedStatType::ArmorConstant: value = expectedStatItr->second->ArmorConstant; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::ArmorConstantMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::ArmorConstantMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->ArmorConstantMod; break; @@ -2418,7 +2424,8 @@ float DB2Manager::EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int3 case ExpectedStatType::CreatureSpellDamage: value = expectedStatItr->second->CreatureSpellDamage; if (contentTuningMods) - value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, ExpectedStatModReducer<&ExpectedStatModEntry::CreatureSpellDamageMod>()); + value *= std::accumulate(contentTuningMods->begin(), contentTuningMods->end(), 1.0f, + ExpectedStatModReducer<&ExpectedStatModEntry::CreatureSpellDamageMod>(mythicPlusMilestoneSeason)); if (classMod) value *= classMod->CreatureSpellDamageMod; break; @@ -2463,171 +2470,6 @@ HeirloomEntry const* DB2Manager::GetHeirloomByItemId(uint32 itemId) const return Trinity::Containers::MapGetValuePtr(_heirlooms, itemId); } -DB2Manager::ItemBonusList const* DB2Manager::GetItemBonusList(uint32 bonusListId) const -{ - return Trinity::Containers::MapGetValuePtr(_itemBonusLists, bonusListId); -} - -uint32 DB2Manager::GetItemBonusListForItemLevelDelta(int16 delta) const -{ - auto itr = _itemLevelDeltaToBonusListContainer.find(delta); - if (itr != _itemLevelDeltaToBonusListContainer.end()) - return itr->second; - - return 0; -} - -template<typename Visitor> -void VisitItemBonusTree(uint32 itemBonusTreeId, bool visitChildren, Visitor visitor) -{ - auto treeItr = _itemBonusTrees.find(itemBonusTreeId); - if (treeItr == _itemBonusTrees.end()) - return; - - for (ItemBonusTreeNodeEntry const* bonusTreeNode : treeItr->second) - { - visitor(bonusTreeNode); - if (visitChildren && bonusTreeNode->ChildItemBonusTreeID) - VisitItemBonusTree(bonusTreeNode->ChildItemBonusTreeID, true, visitor); - } -} - -std::set<uint32> DB2Manager::GetDefaultItemBonusTree(uint32 itemId, ItemContext itemContext) const -{ - std::set<uint32> bonusListIDs; - - ItemSparseEntry const* proto = sItemSparseStore.LookupEntry(itemId); - if (!proto) - return bonusListIDs; - - auto itemIdRange = _itemToBonusTree.equal_range(itemId); - if (itemIdRange.first == itemIdRange.second) - return bonusListIDs; - - uint16 itemLevelSelectorId = 0; - for (auto itemTreeItr = itemIdRange.first; itemTreeItr != itemIdRange.second; ++itemTreeItr) - { - uint32 matchingNodes = 0; - VisitItemBonusTree(itemTreeItr->second, false, [itemContext, &matchingNodes](ItemBonusTreeNodeEntry const* bonusTreeNode) - { - if (ItemContext(bonusTreeNode->ItemContext) == ItemContext::NONE || itemContext == ItemContext(bonusTreeNode->ItemContext)) - ++matchingNodes; - }); - - if (matchingNodes != 1) - continue; - - VisitItemBonusTree(itemTreeItr->second, true, [itemContext, &bonusListIDs, &itemLevelSelectorId](ItemBonusTreeNodeEntry const* bonusTreeNode) - { - ItemContext requiredContext = ItemContext(bonusTreeNode->ItemContext) != ItemContext::Force_to_NONE ? ItemContext(bonusTreeNode->ItemContext) : ItemContext::NONE; - if (ItemContext(bonusTreeNode->ItemContext) != ItemContext::NONE && itemContext != requiredContext) - return; - - if (bonusTreeNode->ChildItemBonusListID) - { - bonusListIDs.insert(bonusTreeNode->ChildItemBonusListID); - } - else if (bonusTreeNode->ChildItemLevelSelectorID) - { - itemLevelSelectorId = bonusTreeNode->ChildItemLevelSelectorID; - } - }); - } - - if (ItemLevelSelectorEntry const* selector = sItemLevelSelectorStore.LookupEntry(itemLevelSelectorId)) - { - int16 delta = int16(selector->MinItemLevel) - proto->ItemLevel; - - if (uint32 bonus = GetItemBonusListForItemLevelDelta(delta)) - bonusListIDs.insert(bonus); - - if (ItemLevelSelectorQualitySetEntry const* selectorQualitySet = sItemLevelSelectorQualitySetStore.LookupEntry(selector->ItemLevelSelectorQualitySetID)) - { - auto itemSelectorQualities = _itemLevelQualitySelectorQualities.find(selector->ItemLevelSelectorQualitySetID); - if (itemSelectorQualities != _itemLevelQualitySelectorQualities.end()) - { - ItemQualities quality = ITEM_QUALITY_UNCOMMON; - if (selector->MinItemLevel >= selectorQualitySet->IlvlEpic) - quality = ITEM_QUALITY_EPIC; - else if (selector->MinItemLevel >= selectorQualitySet->IlvlRare) - quality = ITEM_QUALITY_RARE; - - auto itemSelectorQuality = std::lower_bound(itemSelectorQualities->second.begin(), itemSelectorQualities->second.end(), - quality, ItemLevelSelectorQualityEntryComparator{}); - - if (itemSelectorQuality != itemSelectorQualities->second.end()) - bonusListIDs.insert((*itemSelectorQuality)->QualityItemBonusListID); - } - } - - if (AzeriteUnlockMappingEntry const* azeriteUnlockMapping = Trinity::Containers::MapGetValuePtr(_azeriteUnlockMappings, std::make_pair(proto->ID, itemContext))) - { - switch (proto->InventoryType) - { - case INVTYPE_HEAD: - bonusListIDs.insert(azeriteUnlockMapping->ItemBonusListHead); - break; - case INVTYPE_SHOULDERS: - bonusListIDs.insert(azeriteUnlockMapping->ItemBonusListShoulders); - break; - case INVTYPE_CHEST: - case INVTYPE_ROBE: - bonusListIDs.insert(azeriteUnlockMapping->ItemBonusListChest); - break; - } - } - } - - return bonusListIDs; -} - -std::set<uint32> DB2Manager::GetAllItemBonusTreeBonuses(uint32 itemBonusTreeId) const -{ - std::set<uint32> bonusListIDs; - VisitItemBonusTree(itemBonusTreeId, true, [&bonusListIDs](ItemBonusTreeNodeEntry const* bonusTreeNode) - { - if (bonusTreeNode->ChildItemBonusListID) - bonusListIDs.insert(bonusTreeNode->ChildItemBonusListID); - }); - return bonusListIDs; -} - -void LoadAzeriteEmpoweredItemUnlockMappings(std::unordered_map<int32, std::vector<AzeriteUnlockMappingEntry const*>> const& azeriteUnlockMappingsBySet, uint32 itemId) -{ - auto itemIdRange = _itemToBonusTree.equal_range(itemId); - if (itemIdRange.first == itemIdRange.second) - return; - - for (auto itemTreeItr = itemIdRange.first; itemTreeItr != itemIdRange.second; ++itemTreeItr) - { - VisitItemBonusTree(itemTreeItr->second, true, [&azeriteUnlockMappingsBySet, itemId](ItemBonusTreeNodeEntry const* bonusTreeNode) - { - if (!bonusTreeNode->ChildItemBonusListID && bonusTreeNode->ChildItemLevelSelectorID) - { - ItemLevelSelectorEntry const* selector = sItemLevelSelectorStore.LookupEntry(bonusTreeNode->ChildItemLevelSelectorID); - if (!selector) - return; - - if (std::vector<AzeriteUnlockMappingEntry const*> const* azeriteUnlockMappings = Trinity::Containers::MapGetValuePtr(azeriteUnlockMappingsBySet, selector->AzeriteUnlockMappingSet)) - { - AzeriteUnlockMappingEntry const* selectedAzeriteUnlockMapping = nullptr; - for (AzeriteUnlockMappingEntry const* azeriteUnlockMapping : *azeriteUnlockMappings) - { - if (azeriteUnlockMapping->ItemLevel > selector->MinItemLevel || - (selectedAzeriteUnlockMapping != nullptr && selectedAzeriteUnlockMapping->ItemLevel > azeriteUnlockMapping->ItemLevel)) - continue; - - selectedAzeriteUnlockMapping = azeriteUnlockMapping; - } - - if (selectedAzeriteUnlockMapping) - _azeriteUnlockMappings[std::make_pair(itemId, ItemContext(bonusTreeNode->ItemContext))] = selectedAzeriteUnlockMapping; - } - } - }); - } -} - ItemChildEquipmentEntry const* DB2Manager::GetItemChildEquipment(uint32 itemId) const { return Trinity::Containers::MapGetValuePtr(_itemChildEquipment, itemId); @@ -3490,11 +3332,6 @@ bool ChrClassesXPowerTypesEntryComparator::Compare(ChrClassesXPowerTypesEntry co return left->PowerType < right->PowerType; } -bool ItemLevelSelectorQualityEntryComparator::Compare(ItemLevelSelectorQualityEntry const* left, ItemLevelSelectorQualityEntry const* right) -{ - return left->Quality < right->Quality; -} - TaxiMask::TaxiMask() { if (sTaxiNodesStore.GetNumRows()) diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index f8c07ae3432..6805fb46d18 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -57,6 +57,7 @@ TC_GAME_API extern DB2Storage<AzeriteKnowledgeMultiplierEntry> sAzeriteKnow TC_GAME_API extern DB2Storage<AzeriteItemMilestonePowerEntry> sAzeriteItemMilestonePowerStore; TC_GAME_API extern DB2Storage<AzeriteLevelInfoEntry> sAzeriteLevelInfoStore; TC_GAME_API extern DB2Storage<AzeritePowerEntry> sAzeritePowerStore; +TC_GAME_API extern DB2Storage<AzeriteUnlockMappingEntry> sAzeriteUnlockMappingStore; TC_GAME_API extern DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore; TC_GAME_API extern DB2Storage<BannedAddonsEntry> sBannedAddonsStore; TC_GAME_API extern DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore; @@ -68,6 +69,7 @@ TC_GAME_API extern DB2Storage<BattlePetSpeciesStateEntry> sBattlePetSp TC_GAME_API extern DB2Storage<BattlemasterListEntry> sBattlemasterListStore; TC_GAME_API extern DB2Storage<BroadcastTextEntry> sBroadcastTextStore; TC_GAME_API extern DB2Storage<Cfg_RegionsEntry> sCfgRegionsStore; +TC_GAME_API extern DB2Storage<ChallengeModeItemBonusOverrideEntry> sChallengeModeItemBonusOverrideStore; TC_GAME_API extern DB2Storage<CharTitlesEntry> sCharTitlesStore; TC_GAME_API extern DB2Storage<CharacterLoadoutEntry> sCharacterLoadoutStore; TC_GAME_API extern DB2Storage<CharacterLoadoutItemEntry> sCharacterLoadoutItemStore; @@ -135,6 +137,12 @@ TC_GAME_API extern DB2Storage<ItemArmorQualityEntry> sItemArmorQu TC_GAME_API extern DB2Storage<ItemArmorShieldEntry> sItemArmorShieldStore; TC_GAME_API extern DB2Storage<ItemArmorTotalEntry> sItemArmorTotalStore; TC_GAME_API extern DB2Storage<ItemBagFamilyEntry> sItemBagFamilyStore; +TC_GAME_API extern DB2Storage<ItemBonusEntry> sItemBonusStore; +TC_GAME_API extern DB2Storage<ItemBonusListGroupEntryEntry> sItemBonusListGroupEntryStore; +TC_GAME_API extern DB2Storage<ItemBonusListLevelDeltaEntry> sItemBonusListLevelDeltaStore; +TC_GAME_API extern DB2Storage<ItemBonusTreeEntry> sItemBonusTreeStore; +TC_GAME_API extern DB2Storage<ItemBonusTreeNodeEntry> sItemBonusTreeNodeStore; +TC_GAME_API extern DB2Storage<ItemContextPickerEntryEntry> sItemContextPickerEntryStore; TC_GAME_API extern DB2Storage<ItemDamageAmmoEntry> sItemDamageAmmoStore; TC_GAME_API extern DB2Storage<ItemDamageOneHandEntry> sItemDamageOneHandStore; TC_GAME_API extern DB2Storage<ItemDamageOneHandCasterEntry> sItemDamageOneHandCasterStore; @@ -145,6 +153,9 @@ TC_GAME_API extern DB2Storage<ItemEffectEntry> sItemEffectS TC_GAME_API extern DB2Storage<ItemNameDescriptionEntry> sItemNameDescriptionStore; TC_GAME_API extern DB2Storage<ItemEntry> sItemStore; TC_GAME_API extern DB2Storage<ItemExtendedCostEntry> sItemExtendedCostStore; +TC_GAME_API extern DB2Storage<ItemLevelSelectorEntry> sItemLevelSelectorStore; +TC_GAME_API extern DB2Storage<ItemLevelSelectorQualityEntry> sItemLevelSelectorQualityStore; +TC_GAME_API extern DB2Storage<ItemLevelSelectorQualitySetEntry> sItemLevelSelectorQualitySetStore; TC_GAME_API extern DB2Storage<ItemLimitCategoryEntry> sItemLimitCategoryStore; TC_GAME_API extern DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore; TC_GAME_API extern DB2Storage<ItemModifiedAppearanceExtraEntry> sItemModifiedAppearanceExtraStore; @@ -155,6 +166,7 @@ TC_GAME_API extern DB2Storage<ItemSetSpellEntry> sItemSetSpel TC_GAME_API extern DB2Storage<ItemSparseEntry> sItemSparseStore; TC_GAME_API extern DB2Storage<ItemSpecEntry> sItemSpecStore; TC_GAME_API extern DB2Storage<ItemSpecOverrideEntry> sItemSpecOverrideStore; +TC_GAME_API extern DB2Storage<ItemXBonusTreeEntry> sItemXBonusTreeStore; TC_GAME_API extern DB2Storage<ItemXItemEffectEntry> sItemXItemEffectStore; TC_GAME_API extern DB2Storage<JournalEncounterEntry> sJournalEncounterStore; TC_GAME_API extern DB2Storage<JournalEncounterSectionEntry> sJournalEncounterSectionStore; @@ -175,12 +187,14 @@ TC_GAME_API extern DB2Storage<ModifierTreeEntry> sModifierTre TC_GAME_API extern DB2Storage<MountCapabilityEntry> sMountCapabilityStore; TC_GAME_API extern DB2Storage<MountEntry> sMountStore; TC_GAME_API extern DB2Storage<MovieEntry> sMovieStore; +TC_GAME_API extern DB2Storage<MythicPlusSeasonEntry> sMythicPlusSeasonStore; TC_GAME_API extern DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore; TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonReputationStore; TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore; TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore; TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore; TC_GAME_API extern DB2Storage<PowerTypeEntry> sPowerTypeStore; +TC_GAME_API extern DB2Storage<PvpSeasonEntry> sPvpSeasonStore; TC_GAME_API extern DB2Storage<PvpTalentEntry> sPvpTalentStore; TC_GAME_API extern DB2Storage<PvpTalentCategoryEntry> sPvpTalentCategoryStore; TC_GAME_API extern DB2Storage<PvpTalentSlotUnlockEntry> sPvpTalentSlotUnlockStore; @@ -374,7 +388,6 @@ public: using HotfixContainer = std::map<int32, std::vector<HotfixRecord>>; using FriendshipRepReactionSet = std::set<FriendshipRepReactionEntry const*, FriendshipRepReactionEntryComparator>; - using ItemBonusList = std::vector<ItemBonusEntry const*>; using MapDifficultyConditionsContainer = std::vector<std::pair<uint32, PlayerConditionEntry const*>>; using MountTypeXCapabilitySet = std::set<MountTypeXCapabilityEntry const*, MountTypeXCapabilityEntryComparator>; using MountXDisplayContainer = std::vector<MountXDisplayEntry const*>; @@ -418,23 +431,21 @@ public: static char const* GetChrRaceName(uint8 race, LocaleConstant locale = DEFAULT_LOCALE); ChrSpecializationEntry const* GetChrSpecializationByIndex(uint32 class_, uint32 index) const; ChrSpecializationEntry const* GetDefaultChrSpecializationForClass(uint32 class_) const; - Optional<ContentTuningLevels> GetContentTuningData(uint32 contentTuningId, uint32 replacementConditionMask, bool forItem = false) const; + uint32 GetRedirectedContentTuningId(uint32 contentTuningId, uint32 redirectFlag) const; + Optional<ContentTuningLevels> GetContentTuningData(uint32 contentTuningId, uint32 redirectFlag, bool forItem = false) const; + bool HasContentTuningLabel(uint32 contentTuningId, int32 label) const; static char const* GetCreatureFamilyPetName(uint32 petfamily, LocaleConstant locale); CurrencyContainerEntry const* GetCurrencyContainerForCurrencyQuantity(uint32 currencyId, int32 quantity) const; std::pair<float, float> GetCurveXAxisRange(uint32 curveId) const; float GetCurveValueAt(uint32 curveId, float x) const; EmotesTextSoundEntry const* GetTextSoundEmoteFor(uint32 emote, uint8 race, uint8 gender, uint8 class_) const; - float EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int32 expansion, uint32 contentTuningId, Classes unitClass) const; + float EvaluateExpectedStat(ExpectedStatType stat, uint32 level, int32 expansion, uint32 contentTuningId, Classes unitClass, int32 mythicPlusMilestoneSeason) const; std::vector<uint32> const* GetFactionTeamList(uint32 faction) const; FriendshipRepReactionSet const* GetFriendshipRepReactions(uint32 friendshipRepID) const; uint32 GetGlobalCurveId(GlobalCurve globalCurveType) const; std::vector<uint32> const* GetGlyphBindableSpells(uint32 glyphPropertiesId) const; std::vector<uint32> const* GetGlyphRequiredSpecs(uint32 glyphPropertiesId) const; HeirloomEntry const* GetHeirloomByItemId(uint32 itemId) const; - ItemBonusList const* GetItemBonusList(uint32 bonusListId) const; - uint32 GetItemBonusListForItemLevelDelta(int16 delta) const; - std::set<uint32> GetDefaultItemBonusTree(uint32 itemId, ItemContext itemContext) const; - std::set<uint32> GetAllItemBonusTreeBonuses(uint32 itemBonusTreeId) const; ItemChildEquipmentEntry const* GetItemChildEquipment(uint32 itemId) const; ItemClassEntry const* GetItemClassByOldEnum(uint32 itemClass) const; bool HasItemCurrencyCost(uint32 itemId) const; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index a458abee76c..315b5dbd532 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -555,6 +555,18 @@ struct Cfg_RegionsEntry uint32 ChallengeOrigin; }; +struct ChallengeModeItemBonusOverrideEntry +{ + uint32 ID; + int32 ItemBonusTreeGroupID; + int32 DstItemBonusTreeID; + int8 Type; + int32 Value; + int32 MythicPlusSeasonID; + int32 PvPSeasonID; + uint32 SrcItemBonusTreeID; +}; + struct CharTitlesEntry { uint32 ID; @@ -858,6 +870,15 @@ struct CinematicSequencesEntry std::array<uint16, 8> Camera; }; +struct ConditionalContentTuningEntry +{ + uint32 ID; + int32 OrderIndex; + int32 RedirectContentTuningID; + int32 RedirectFlag; + uint32 ParentContentTuningID; +}; + struct ContentTuningEntry { uint32 ID; @@ -898,6 +919,13 @@ struct ContentTuningXExpectedEntry uint32 ContentTuningID; }; +struct ContentTuningXLabelEntry +{ + uint32 ID; + int32 LabelID; + uint32 ContentTuningID; +}; + struct ConversationLineEntry { uint32 ID; @@ -2062,18 +2090,18 @@ struct ItemBonusEntry }; // new item upgrade system -//struct ItemBonusListGroupEntryEntry -//{ -// uint32 ID; -// int32 ItemBonusListGroupID; -// int32 ItemBonusListID; -// int32 ItemLevelSelectorID; -// int32 SequenceValue; -// int32 ItemExtendedCostID; -// int32 PlayerConditionID; -// int32 Flags; -// int32 ItemLogicalCostGroupID; -//}; +struct ItemBonusListGroupEntryEntry +{ + uint32 ID; + int32 ItemBonusListGroupID; + int32 ItemBonusListID; + int32 ItemLevelSelectorID; + int32 SequenceValue; + int32 ItemExtendedCostID; + int32 PlayerConditionID; + int32 Flags; + int32 ItemLogicalCostGroupID; +}; struct ItemBonusListLevelDeltaEntry { @@ -2088,6 +2116,13 @@ struct ItemBonusListLevelDeltaEntry // int32 ItemID; //}; +struct ItemBonusTreeEntry +{ + uint32 ID; + int32 Flags; + int32 InventoryTypeSlotMask; +}; + struct ItemBonusTreeNodeEntry { uint32 ID; @@ -2097,8 +2132,8 @@ struct ItemBonusTreeNodeEntry uint16 ChildItemLevelSelectorID; int32 ChildItemBonusListGroupID; int32 IblGroupPointsModSetID; - int32 Unknown1010_1; - int32 Unknown1010_2; + int32 MinMythicPlusLevel; + int32 MaxMythicPlusLevel; uint32 ParentItemBonusTreeID; }; @@ -2119,6 +2154,18 @@ struct ItemClassEntry uint8 Flags; }; +struct ItemContextPickerEntryEntry +{ + uint32 ID; + uint8 ItemCreationContext; + uint8 OrderIndex; + int32 PVal; + int32 LabelID; + uint32 Flags; + uint32 PlayerConditionID; + uint32 ItemContextPickerID; +}; + struct ItemCurrencyCostEntry { uint32 ID; @@ -2809,6 +2856,14 @@ struct MovieEntry int32 SubtitleFileFormat; }; +struct MythicPlusSeasonEntry +{ + uint32 ID; + int32 MilestoneSeason; + int32 ExpansionLevel; + int32 HeroicLFGDungeonMinGear; +}; + struct NameGenEntry { uint32 ID; @@ -3027,6 +3082,14 @@ struct PVPItemEntry uint8 ItemLevelDelta; }; +struct PvpSeasonEntry +{ + uint32 ID; + int32 MilestoneSeason; + int32 AllianceAchievementID; + int32 HordeAchievementID; +}; + struct PvpTalentEntry { LocalizedString Description; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 522314d8687..708d3ab4c48 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2953,7 +2953,7 @@ uint64 Creature::GetMaxHealthByLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); + float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); return std::max(baseHealth * creatureDifficulty->HealthModifier, 1.0f); } @@ -2973,7 +2973,7 @@ float Creature::GetBaseDamageForLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); + return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); } float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const @@ -2990,7 +2990,7 @@ float Creature::GetBaseArmorForLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); + float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); return baseArmor * creatureDifficulty->ArmorModifier; } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 665e3244125..e7726b1f595 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -36,6 +36,7 @@ #include "GridNotifiersImpl.h" #include "Group.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "Log.h" #include "Loot.h" #include "LootMgr.h" @@ -1459,9 +1460,10 @@ Loot* GameObject::GetFishLoot(Player* lootOwner) Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING, nullptr); uint32 areaId = GetAreaId(); + ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner); while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId)) { - fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true); + fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext); if (!fishLoot->isLooted()) break; @@ -1469,7 +1471,7 @@ Loot* GameObject::GetFishLoot(Player* lootOwner) } if (fishLoot->isLooted()) - fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true); + fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext); return fishLoot; } @@ -1481,9 +1483,10 @@ Loot* GameObject::GetFishLootJunk(Player* lootOwner) Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING_JUNK, nullptr); uint32 areaId = GetAreaId(); + ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner); while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId)) { - fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH); + fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext); if (!fishLoot->isLooted()) break; @@ -1491,7 +1494,7 @@ Loot* GameObject::GetFishLootJunk(Player* lootOwner) } if (fishLoot->isLooted()) - fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH); + fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext); return fishLoot; } @@ -2240,7 +2243,7 @@ void GameObject::Use(Unit* user) m_loot.reset(loot); loot->SetDungeonEncounterId(info->chest.DungeonEncounter); - loot->FillLoot(info->GetLootId(), LootTemplates_Gameobject, player, !groupRules, false, GetLootMode(), GetMap()->GetDifficultyLootItemContext()); + loot->FillLoot(info->GetLootId(), LootTemplates_Gameobject, player, !groupRules, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player)); if (GetLootMode() > 0) if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) @@ -2271,7 +2274,7 @@ void GameObject::Use(Unit* user) m_personalLoot = GenerateDungeonEncounterPersonalLoot(info->chest.DungeonEncounter, info->chest.chestPersonalLoot, LootTemplates_Gameobject, LOOT_CHEST, this, addon ? addon->Mingold : 0, addon ? addon->Maxgold : 0, - GetLootMode(), GetMap()->GetDifficultyLootItemContext(), tappers); + GetLootMode(), GetMap()->GetMapDifficulty(), tappers); } else { @@ -2279,7 +2282,7 @@ void GameObject::Use(Unit* user) m_personalLoot[player->GetGUID()].reset(loot); loot->SetDungeonEncounterId(info->chest.DungeonEncounter); - loot->FillLoot(info->chest.chestPersonalLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), GetMap()->GetDifficultyLootItemContext()); + loot->FillLoot(info->chest.chestPersonalLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player)); if (GetLootMode() > 0 && addon) loot->generateMoneyLoot(addon->Mingold, addon->Maxgold); @@ -2292,7 +2295,7 @@ void GameObject::Use(Unit* user) if (info->chest.chestPushLoot) { Loot pushLoot(GetMap(), GetGUID(), LOOT_CHEST, nullptr); - pushLoot.FillLoot(info->chest.chestPushLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), GetMap()->GetDifficultyLootItemContext()); + pushLoot.FillLoot(info->chest.chestPushLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player)); pushLoot.AutoStore(player, NULL_BAG, NULL_SLOT); } @@ -2798,7 +2801,7 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_FISHINGHOLE, nullptr); - loot->FillLoot(GetGOInfo()->GetLootId(), LootTemplates_Gameobject, player, true); + loot->FillLoot(GetGOInfo()->GetLootId(), LootTemplates_Gameobject, player, true, false, LOOT_MODE_DEFAULT, ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player)); m_personalLoot[player->GetGUID()].reset(loot); player->SendLoot(*loot); @@ -2981,7 +2984,7 @@ void GameObject::Use(Unit* user) Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, nullptr); m_personalLoot[player->GetGUID()].reset(loot); - loot->FillLoot(info->gatheringNode.chestLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), GetMap()->GetDifficultyLootItemContext()); + loot->FillLoot(info->gatheringNode.chestLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player)); } if (info->gatheringNode.triggeredEvent) diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index c8d22b1a65b..7ea5665984b 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -28,6 +28,7 @@ #include "DB2Stores.h" #include "GameTables.h" #include "GameTime.h" +#include "ItemBonusMgr.h" #include "ItemEnchantmentMgr.h" #include "ItemPackets.h" #include "Log.h" @@ -1512,19 +1513,17 @@ void Item::SetGem(uint16 slot, ItemDynamicFieldGems const* gem, uint32 gemScalin { case ITEM_ENCHANTMENT_TYPE_BONUS_LIST_ID: { - if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(gemEnchant->EffectArg[i])) - for (ItemBonusEntry const* itemBonus : *bonuses) - if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL) - _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0]; + for (ItemBonusEntry const* itemBonus : ItemBonusMgr::GetItemBonuses(gemEnchant->EffectArg[i])) + if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL) + _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0]; break; } case ITEM_ENCHANTMENT_TYPE_BONUS_LIST_CURVE: { - if (uint32 bonusListId = sDB2Manager.GetItemBonusListForItemLevelDelta(int16(sDB2Manager.GetCurveValueAt(CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS, gemBaseItemLevel + gemBonus.ItemLevelBonus)))) - if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId)) - for (ItemBonusEntry const* itemBonus : *bonuses) - if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL) - _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0]; + if (uint32 bonusListId = ItemBonusMgr::GetItemBonusListForItemLevelDelta(int16(sDB2Manager.GetCurveValueAt(CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS, gemBaseItemLevel + gemBonus.ItemLevelBonus)))) + for (ItemBonusEntry const* itemBonus : ItemBonusMgr::GetItemBonuses(bonusListId)) + if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL) + _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0]; break; } default: @@ -1616,7 +1615,7 @@ void Item::SendTimeUpdate(Player* owner) owner->GetSession()->SendPacket(itemTimeUpdate.Write()); } -Item* Item::CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const* player /*= nullptr*/) +Item* Item::CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const* player /*= nullptr*/, bool addDefaultBonuses /*= true*/) { if (count < 1) return nullptr; //don't create item at zero count @@ -1633,6 +1632,9 @@ Item* Item::CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Play if (item->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), itemEntry, context, player)) { item->SetCount(count); + if (addDefaultBonuses) + item->SetBonuses(ItemBonusMgr::GetBonusListsForItem(itemEntry, context)); + return item; } else @@ -1645,7 +1647,7 @@ Item* Item::CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Play Item* Item::CloneItem(uint32 count, Player const* player /*= nullptr*/) const { - Item* newItem = CreateItem(GetEntry(), count, GetContext(), player); + Item* newItem = CreateItem(GetEntry(), count, GetContext(), player, false); if (!newItem) return nullptr; @@ -1653,6 +1655,7 @@ Item* Item::CloneItem(uint32 count, Player const* player /*= nullptr*/) const newItem->SetGiftCreator(GetGiftCreator()); newItem->ReplaceAllItemFlags(ItemFieldFlags(*m_itemData->DynamicFlags) & ~(ITEM_FIELD_FLAG_REFUNDABLE | ITEM_FIELD_FLAG_BOP_TRADEABLE)); newItem->SetExpiration(m_itemData->Expiration); + newItem->SetBonuses(m_itemData->ItemBonusKey->BonusListIDs); // player CAN be NULL in which case we must not update random properties because that accesses player's item update queue if (player) newItem->SetItemRandomBonusList(m_randomBonusListId); @@ -2513,17 +2516,14 @@ void Item::AddBonuses(uint32 bonusListID) if (std::find(GetBonusListIDs().begin(), GetBonusListIDs().end(), int32(bonusListID)) != GetBonusListIDs().end()) return; - if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListID)) - { - WorldPackets::Item::ItemBonusKey itemBonusKey; - itemBonusKey.ItemID = GetEntry(); - itemBonusKey.BonusListIDs = GetBonusListIDs(); - itemBonusKey.BonusListIDs.push_back(bonusListID); - SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemBonusKey), std::move(itemBonusKey)); - for (ItemBonusEntry const* bonus : *bonuses) - _bonusData.AddBonus(bonus->Type, bonus->Value); - SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemAppearanceModID), _bonusData.AppearanceModID); - } + WorldPackets::Item::ItemBonusKey itemBonusKey; + itemBonusKey.ItemID = GetEntry(); + itemBonusKey.BonusListIDs = GetBonusListIDs(); + itemBonusKey.BonusListIDs.push_back(bonusListID); + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemBonusKey), std::move(itemBonusKey)); + for (ItemBonusEntry const* bonus : ItemBonusMgr::GetItemBonuses(bonusListID)) + _bonusData.AddBonus(bonus->Type, bonus->Value); + SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemAppearanceModID), _bonusData.AppearanceModID); } void Item::SetBonuses(std::vector<int32> bonusListIDs) @@ -2877,9 +2877,8 @@ void BonusData::Initialize(WorldPackets::Item::ItemInstance const& itemInstance) void BonusData::AddBonusList(uint32 bonusListId) { - if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId)) - for (ItemBonusEntry const* bonus : *bonuses) - AddBonus(bonus->Type, bonus->Value); + for (ItemBonusEntry const* bonus : ItemBonusMgr::GetItemBonuses(bonusListId)) + AddBonus(bonus->Type, bonus->Value); } void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values) diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 565c51fd34f..42875e9f74f 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -172,7 +172,7 @@ class TC_GAME_API Item : public Object friend void RemoveItemFromUpdateQueueOf(Item* item, Player* player); public: - static Item* CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const* player = nullptr); + static Item* CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const* player = nullptr, bool addDefaultBonuses = true); Item* CloneItem(uint32 count, Player const* player = nullptr) const; Item(); diff --git a/src/server/game/Entities/Item/ItemBonusMgr.cpp b/src/server/game/Entities/Item/ItemBonusMgr.cpp new file mode 100644 index 00000000000..748fd929647 --- /dev/null +++ b/src/server/game/Entities/Item/ItemBonusMgr.cpp @@ -0,0 +1,509 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ItemBonusMgr.h" +#include "ConditionMgr.h" +#include "DB2Stores.h" +#include "MapUtils.h" +#include "ObjectMgr.h" +#include "Player.h" + +namespace +{ +struct ItemLevelSelectorQualityEntryComparator +{ + bool operator()(ItemLevelSelectorQualityEntry const* left, ItemLevelSelectorQualityEntry const* right) const { return left->Quality < right->Quality; } + bool operator()(ItemLevelSelectorQualityEntry const* left, ItemQualities quality) const { return left->Quality < quality; } +}; + +using ItemLevelSelectorQualities = std::set<ItemLevelSelectorQualityEntry const*, ItemLevelSelectorQualityEntryComparator>; + +std::unordered_multimap<int32 /*azeriteUnlockMappingSetId*/, AzeriteUnlockMappingEntry const*> _azeriteUnlockMappings; +std::unordered_multimap<uint32 /*itemBonusTreeId*/, ChallengeModeItemBonusOverrideEntry const*> _challengeModeItemBonusOverrides; +std::unordered_map<uint32 /*itemBonusListId*/, std::vector<ItemBonusEntry const*>> _itemBonusLists; +std::unordered_multimap<int32, ItemBonusListGroupEntryEntry const*> _itemBonusListGroupEntries; +std::unordered_map<int16 /*itemLevelDelta*/, uint32 /*itemBonusListId*/> _itemLevelDeltaToBonusListContainer; +std::unordered_map<uint32 /*itemLevelSelectorQualitySetId*/, ItemLevelSelectorQualities> _itemLevelQualitySelectorQualities; +std::unordered_map<uint32 /*itemBonusTreeId*/, std::set<ItemBonusTreeNodeEntry const*>> _itemBonusTrees; +std::unordered_multimap<uint32 /*itemId*/, uint32 /*itemBonusTreeId*/> _itemToBonusTree; +} + +namespace ItemBonusMgr +{ +void Load() +{ + for (AzeriteUnlockMappingEntry const* azeriteUnlockMapping : sAzeriteUnlockMappingStore) + _azeriteUnlockMappings.emplace(azeriteUnlockMapping->AzeriteUnlockMappingSetID, azeriteUnlockMapping); + + for (ChallengeModeItemBonusOverrideEntry const* challengeModeItemBonusOverride : sChallengeModeItemBonusOverrideStore) + _challengeModeItemBonusOverrides.emplace(challengeModeItemBonusOverride->SrcItemBonusTreeID, challengeModeItemBonusOverride); + + for (ItemBonusEntry const* bonus : sItemBonusStore) + _itemBonusLists[bonus->ParentItemBonusListID].push_back(bonus); + + for (ItemBonusListGroupEntryEntry const* bonusListGroupEntry : sItemBonusListGroupEntryStore) + _itemBonusListGroupEntries.emplace(bonusListGroupEntry->ItemBonusListGroupID, bonusListGroupEntry); + + for (ItemBonusListLevelDeltaEntry const* itemBonusListLevelDelta : sItemBonusListLevelDeltaStore) + _itemLevelDeltaToBonusListContainer[itemBonusListLevelDelta->ItemLevelDelta] = itemBonusListLevelDelta->ID; + + for (ItemLevelSelectorQualityEntry const* itemLevelSelectorQuality : sItemLevelSelectorQualityStore) + _itemLevelQualitySelectorQualities[itemLevelSelectorQuality->ParentILSQualitySetID].insert(itemLevelSelectorQuality); + + for (ItemBonusTreeNodeEntry const* bonusTreeNode : sItemBonusTreeNodeStore) + _itemBonusTrees[bonusTreeNode->ParentItemBonusTreeID].insert(bonusTreeNode); + + for (ItemXBonusTreeEntry const* itemBonusTreeAssignment : sItemXBonusTreeStore) + _itemToBonusTree.insert({ itemBonusTreeAssignment->ItemID, itemBonusTreeAssignment->ItemBonusTreeID }); +} + +ItemContext GetContextForPlayer(MapDifficultyEntry const* mapDifficulty, Player const* player) +{ + auto evalContext = [](ItemContext currentContext, ItemContext newContext) + { + if (newContext == ItemContext::NONE) + newContext = currentContext; + else if (newContext == ItemContext::Force_to_NONE) + newContext = ItemContext::NONE; + return newContext; + }; + + ItemContext context = ItemContext::NONE; + if (DifficultyEntry const* difficulty = sDifficultyStore.LookupEntry(mapDifficulty->DifficultyID)) + context = evalContext(context, ItemContext(difficulty->ItemContext)); + + context = evalContext(context, ItemContext(mapDifficulty->ItemContext)); + + if (mapDifficulty->ItemContextPickerID) + { + uint32 contentTuningId = sDB2Manager.GetRedirectedContentTuningId(mapDifficulty->ContentTuningID, player->m_playerData->CtrOptions->ContentTuningConditionMask); + + ItemContextPickerEntryEntry const* selectedPickerEntry = nullptr; + for (ItemContextPickerEntryEntry const* itemContextPickerEntry : sItemContextPickerEntryStore) + { + if (itemContextPickerEntry->ItemContextPickerID != uint32(mapDifficulty->ItemContextPickerID)) + continue; + + if (itemContextPickerEntry->PVal <= 0) + continue; + + bool meetsPlayerCondition = false; + if (player) + if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(itemContextPickerEntry->PlayerConditionID)) + meetsPlayerCondition = ConditionMgr::IsPlayerMeetingCondition(player, playerCondition); + + if (itemContextPickerEntry->Flags & 0x1) + meetsPlayerCondition = !meetsPlayerCondition; + + if (!meetsPlayerCondition) + continue; + + if (itemContextPickerEntry->LabelID && !sDB2Manager.HasContentTuningLabel(contentTuningId, itemContextPickerEntry->LabelID)) + continue; + + if (!selectedPickerEntry || selectedPickerEntry->OrderIndex < itemContextPickerEntry->OrderIndex) + selectedPickerEntry = itemContextPickerEntry; + } + + if (selectedPickerEntry) + context = evalContext(context, ItemContext(selectedPickerEntry->ItemCreationContext)); + } + + return context; +} + +std::span<ItemBonusEntry const*> GetItemBonuses(uint32 bonusListId) +{ + if (std::vector<ItemBonusEntry const*>* itemBonusEntries = Trinity::Containers::MapGetValuePtr(_itemBonusLists, bonusListId)) + return std::span(*itemBonusEntries); + + return {}; +} + +uint32 GetItemBonusListForItemLevelDelta(int16 delta) +{ + if (uint32 const* itemBonusListId = Trinity::Containers::MapGetValuePtr(_itemLevelDeltaToBonusListContainer, delta)) + return *itemBonusListId; + + return 0; +} + +bool CanApplyBonusTreeToItem(ItemTemplate const* itemTemplate, uint32 itemBonusTreeId, ItemBonusGenerationParams const& params) +{ + if (ItemBonusTreeEntry const* bonusTree = sItemBonusTreeStore.LookupEntry(itemBonusTreeId)) + { + if (bonusTree->InventoryTypeSlotMask) + if (!(1 << itemTemplate->GetInventoryType() & bonusTree->InventoryTypeSlotMask)) + return false; + + if (bonusTree->Flags & 0x8 && !itemTemplate->HasFlag(ITEM_FLAG2_CASTER_WEAPON)) + return false; + if (bonusTree->Flags & 0x10 && itemTemplate->HasFlag(ITEM_FLAG2_CASTER_WEAPON)) + return false; + if (bonusTree->Flags & 0x20 && !itemTemplate->HasFlag(ITEM_FLAG4_CC_TRINKET)) + return false; + if (bonusTree->Flags & 0x40 && itemTemplate->HasFlag(ITEM_FLAG4_CC_TRINKET)) + return false; + + if (bonusTree->Flags & 0x4) + return true; + } + + if (std::set<ItemBonusTreeNodeEntry const*>* bonusTreeNodes = Trinity::Containers::MapGetValuePtr(_itemBonusTrees, itemBonusTreeId)) + { + bool anyNodeMatched = false; + for (ItemBonusTreeNodeEntry const* bonusTreeNode : *bonusTreeNodes) + { + if (bonusTreeNode->MinMythicPlusLevel > 0) + continue; + + ItemContext nodeContext = ItemContext(bonusTreeNode->ItemContext); + if (nodeContext == ItemContext::NONE || nodeContext == params.Context) + { + if (anyNodeMatched) + return false; + + anyNodeMatched = true; + } + } + } + + return true; +} + +uint32 GetBonusTreeIdOverride(uint32 itemBonusTreeId, ItemBonusGenerationParams const& params) +{ + // TODO: configure seasons globally + if (MythicPlusSeasonEntry const* mythicPlusSeason = sMythicPlusSeasonStore.LookupEntry(0)) + { + int32 selectedLevel = -1; + int32 selectedMilestoneSeason = -1; + ChallengeModeItemBonusOverrideEntry const* selectedItemBonusOverride = nullptr; + for (auto& [_, itemBonusOverride] : Trinity::Containers::MapEqualRange(_challengeModeItemBonusOverrides, itemBonusTreeId)) + { + if (itemBonusOverride->Type != 0) + continue; + + if (itemBonusOverride->Value > params.MythicPlusKeystoneLevel.value_or(-1)) + continue; + + if (itemBonusOverride->MythicPlusSeasonID) + { + MythicPlusSeasonEntry const* overrideSeason = sMythicPlusSeasonStore.LookupEntry(itemBonusOverride->MythicPlusSeasonID); + if (!overrideSeason) + continue; + + if (mythicPlusSeason->MilestoneSeason < overrideSeason->MilestoneSeason) + continue; + + if (selectedMilestoneSeason > overrideSeason->MilestoneSeason) + continue; + + if (selectedMilestoneSeason == overrideSeason->MilestoneSeason) + if (selectedLevel > itemBonusOverride->Value) + continue; + + selectedMilestoneSeason = overrideSeason->MilestoneSeason; + } + else if (selectedLevel > itemBonusOverride->Value) + continue; + + selectedLevel = itemBonusOverride->Value; + selectedItemBonusOverride = itemBonusOverride; + } + + if (selectedItemBonusOverride && selectedItemBonusOverride->DstItemBonusTreeID) + itemBonusTreeId = selectedItemBonusOverride->DstItemBonusTreeID; + } + + // TODO: configure seasons globally + if (PvpSeasonEntry const* pvpSeason = sPvpSeasonStore.LookupEntry(0)) + { + int32 selectedLevel = -1; + int32 selectedMilestoneSeason = -1; + ChallengeModeItemBonusOverrideEntry const* selectedItemBonusOverride = nullptr; + for (auto& [_, itemBonusOverride] : Trinity::Containers::MapEqualRange(_challengeModeItemBonusOverrides, itemBonusTreeId)) + { + if (itemBonusOverride->Type != 1) + continue; + + if (itemBonusOverride->Value > params.PvpTier.value_or(-1)) + continue; + + if (itemBonusOverride->PvPSeasonID) + { + PvpSeasonEntry const* overrideSeason = sPvpSeasonStore.LookupEntry(itemBonusOverride->PvPSeasonID); + if (!overrideSeason) + continue; + + if (pvpSeason->MilestoneSeason < overrideSeason->MilestoneSeason) + continue; + + if (selectedMilestoneSeason > overrideSeason->MilestoneSeason) + continue; + + if (selectedMilestoneSeason == overrideSeason->MilestoneSeason) + if (selectedLevel > itemBonusOverride->Value) + continue; + + selectedMilestoneSeason = overrideSeason->MilestoneSeason; + } + else if (selectedLevel > itemBonusOverride->Value) + continue; + + selectedLevel = itemBonusOverride->Value; + selectedItemBonusOverride = itemBonusOverride; + } + + if (selectedItemBonusOverride && selectedItemBonusOverride->DstItemBonusTreeID) + itemBonusTreeId = selectedItemBonusOverride->DstItemBonusTreeID; + } + + return itemBonusTreeId; +} + +void ApplyBonusTreeHelper(ItemTemplate const* itemTemplate, uint32 itemBonusTreeId, ItemBonusGenerationParams const& params, int32 sequenceLevel, uint32* itemLevelSelectorId, std::vector<int32>* bonusListIDs) +{ + uint32 originalItemBonusTreeId = itemBonusTreeId; + + // override bonus tree with season specific values + itemBonusTreeId = GetBonusTreeIdOverride(itemBonusTreeId, params); + + if (!CanApplyBonusTreeToItem(itemTemplate, itemBonusTreeId, params)) + return; + + auto treeItr = _itemBonusTrees.find(itemBonusTreeId); + if (treeItr == _itemBonusTrees.end()) + return; + + for (ItemBonusTreeNodeEntry const* bonusTreeNode : treeItr->second) + { + ItemContext nodeContext = ItemContext(bonusTreeNode->ItemContext); + ItemContext requiredContext = nodeContext != ItemContext::Force_to_NONE ? nodeContext : ItemContext::NONE; + if (nodeContext != ItemContext::NONE && params.Context != requiredContext) + continue; + + if (params.MythicPlusKeystoneLevel) + { + if (bonusTreeNode->MinMythicPlusLevel && params.MythicPlusKeystoneLevel < bonusTreeNode->MinMythicPlusLevel) + continue; + + if (bonusTreeNode->MaxMythicPlusLevel && params.MythicPlusKeystoneLevel > bonusTreeNode->MaxMythicPlusLevel) + continue; + } + + if (bonusTreeNode->ChildItemBonusTreeID) + ApplyBonusTreeHelper(itemTemplate, bonusTreeNode->ChildItemBonusTreeID, params, sequenceLevel, itemLevelSelectorId, bonusListIDs); + else if (bonusTreeNode->ChildItemBonusListID) + bonusListIDs->push_back(bonusTreeNode->ChildItemBonusListID); + else if (bonusTreeNode->ChildItemLevelSelectorID) + *itemLevelSelectorId = bonusTreeNode->ChildItemLevelSelectorID; + else if (bonusTreeNode->ChildItemBonusListGroupID) + { + int32 resolvedSequenceLevel = sequenceLevel; + switch (originalItemBonusTreeId) + { + case 4001: + resolvedSequenceLevel = 1; + break; + case 4079: + if (params.MythicPlusKeystoneLevel) + { + switch (bonusTreeNode->IblGroupPointsModSetID) + { + case 2909: // MythicPlus_End_of_Run levels 2-8 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(62951, *params.MythicPlusKeystoneLevel); + break; + case 2910: // MythicPlus_End_of_Run levels 9-16 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(62952, *params.MythicPlusKeystoneLevel); + break; + case 2911: // MythicPlus_End_of_Run levels 17-20 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(62954, *params.MythicPlusKeystoneLevel); + break; + case 3007: // MythicPlus_Jackpot (weekly reward) levels 2-7 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(64388, *params.MythicPlusKeystoneLevel); + break; + case 3008: // MythicPlus_Jackpot (weekly reward) levels 8-15 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(64389, *params.MythicPlusKeystoneLevel); + break; + case 3009: // MythicPlus_Jackpot (weekly reward) levels 16-20 + resolvedSequenceLevel = sDB2Manager.GetCurveValueAt(64395, *params.MythicPlusKeystoneLevel); + break; + default: + break; + } + } + break; + case 4125: + resolvedSequenceLevel = 2; + break; + case 4126: + resolvedSequenceLevel = 3; + break; + case 4127: + resolvedSequenceLevel = 4; + break; + case 4128: + switch (params.Context) + { + case ItemContext::Raid_Normal: + case ItemContext::Raid_Raid_Finder: + case ItemContext::Raid_Heroic: + resolvedSequenceLevel = 2; + break; + case ItemContext::Raid_Mythic: + resolvedSequenceLevel = 6; + break; + default: + break; + } + break; + case 4140: + switch (params.Context) + { + case ItemContext::Dungeon_Normal: + resolvedSequenceLevel = 2; + break; + case ItemContext::Dungeon_Mythic: + resolvedSequenceLevel = 4; + break; + default: + break; + } + break; + default: + break; + } + + for (auto const& [_, bonusListGroupEntry] : Trinity::Containers::MapEqualRange(_itemBonusListGroupEntries, bonusTreeNode->ChildItemBonusListGroupID)) + { + if ((resolvedSequenceLevel > 0 || bonusListGroupEntry->SequenceValue <= 0) && resolvedSequenceLevel != bonusListGroupEntry->SequenceValue) + continue; + + *itemLevelSelectorId = bonusListGroupEntry->ItemLevelSelectorID; + bonusListIDs->push_back(bonusListGroupEntry->ItemBonusListID); + break; + } + } + } +} + +int32 GetAzeriteUnlockBonusList(uint16 azeriteUnlockMappingSetId, uint16 minItemLevel, InventoryType inventoryType) +{ + AzeriteUnlockMappingEntry const* selectedAzeriteUnlockMapping = nullptr; + for (auto [_, azeriteUnlockMapping] : Trinity::Containers::MapEqualRange(_azeriteUnlockMappings, azeriteUnlockMappingSetId)) + { + if (minItemLevel < azeriteUnlockMapping->ItemLevel) + continue; + + if (selectedAzeriteUnlockMapping && selectedAzeriteUnlockMapping->ItemLevel > azeriteUnlockMapping->ItemLevel) + continue; + + selectedAzeriteUnlockMapping = azeriteUnlockMapping; + } + + if (selectedAzeriteUnlockMapping) + { + switch (inventoryType) + { + case INVTYPE_HEAD: + return selectedAzeriteUnlockMapping->ItemBonusListHead; + case INVTYPE_SHOULDERS: + return selectedAzeriteUnlockMapping->ItemBonusListShoulders; + case INVTYPE_CHEST: + case INVTYPE_ROBE: + return selectedAzeriteUnlockMapping->ItemBonusListChest; + default: + break; + } + } + + return 0; +} + +std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const& params) +{ + std::vector<int32> bonusListIDs; + + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId); + if (!itemTemplate) + return bonusListIDs; + + uint32 itemLevelSelectorId = 0; + + for (auto [_, itemBonusTreeId] : Trinity::Containers::MapEqualRange(_itemToBonusTree, itemId)) + ApplyBonusTreeHelper(itemTemplate, itemBonusTreeId, params, 0, &itemLevelSelectorId, &bonusListIDs); + + if (ItemLevelSelectorEntry const* selector = sItemLevelSelectorStore.LookupEntry(itemLevelSelectorId)) + { + int16 delta = int16(selector->MinItemLevel) - int16(itemTemplate->GetBaseItemLevel()); + + if (uint32 bonus = GetItemBonusListForItemLevelDelta(delta)) + bonusListIDs.push_back(bonus); + + if (ItemLevelSelectorQualitySetEntry const* selectorQualitySet = sItemLevelSelectorQualitySetStore.LookupEntry(selector->ItemLevelSelectorQualitySetID)) + { + auto itemSelectorQualities = _itemLevelQualitySelectorQualities.find(selector->ItemLevelSelectorQualitySetID); + if (itemSelectorQualities != _itemLevelQualitySelectorQualities.end()) + { + ItemQualities quality = ITEM_QUALITY_UNCOMMON; + if (selector->MinItemLevel >= selectorQualitySet->IlvlEpic) + quality = ITEM_QUALITY_EPIC; + else if (selector->MinItemLevel >= selectorQualitySet->IlvlRare) + quality = ITEM_QUALITY_RARE; + + auto itemSelectorQuality = std::lower_bound(itemSelectorQualities->second.begin(), itemSelectorQualities->second.end(), + quality, ItemLevelSelectorQualityEntryComparator{}); + + if (itemSelectorQuality != itemSelectorQualities->second.end()) + bonusListIDs.push_back((*itemSelectorQuality)->QualityItemBonusListID); + } + } + + if (int32 azeriteUnlockBonusListId = GetAzeriteUnlockBonusList(selector->AzeriteUnlockMappingSet, selector->MinItemLevel, itemTemplate->GetInventoryType())) + bonusListIDs.push_back(azeriteUnlockBonusListId); + } + + return bonusListIDs; +} + +template<typename Visitor> +void VisitItemBonusTree(uint32 itemBonusTreeId, Visitor visitor) +{ + auto treeItr = _itemBonusTrees.find(itemBonusTreeId); + if (treeItr == _itemBonusTrees.end()) + return; + + for (ItemBonusTreeNodeEntry const* bonusTreeNode : treeItr->second) + { + visitor(bonusTreeNode); + if (bonusTreeNode->ChildItemBonusTreeID) + VisitItemBonusTree(bonusTreeNode->ChildItemBonusTreeID, visitor); + } +} + +std::vector<int32> GetAllBonusListsForTree(uint32 itemBonusTreeId) +{ + std::vector<int32> bonusListIDs; + VisitItemBonusTree(itemBonusTreeId, [&bonusListIDs](ItemBonusTreeNodeEntry const* bonusTreeNode) + { + if (bonusTreeNode->ChildItemBonusListID) + bonusListIDs.push_back(bonusTreeNode->ChildItemBonusListID); + }); + return bonusListIDs; +} +} diff --git a/src/server/game/Entities/Item/ItemBonusMgr.h b/src/server/game/Entities/Item/ItemBonusMgr.h new file mode 100644 index 00000000000..08344547d14 --- /dev/null +++ b/src/server/game/Entities/Item/ItemBonusMgr.h @@ -0,0 +1,55 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_ITEM_BONUS_MGR_H +#define TRINITY_ITEM_BONUS_MGR_H + +#include "Define.h" +#include "Optional.h" +#include <span> +#include <vector> + +class Player; +struct ItemBonusEntry; +struct MapDifficultyEntry; +enum class ItemContext : uint8; + +namespace ItemBonusMgr +{ +TC_GAME_API void Load(); + +TC_GAME_API ItemContext GetContextForPlayer(MapDifficultyEntry const* mapDifficulty, Player const* player); + +TC_GAME_API std::span<ItemBonusEntry const*> GetItemBonuses(uint32 bonusListId); + +TC_GAME_API uint32 GetItemBonusListForItemLevelDelta(int16 delta); + +struct ItemBonusGenerationParams +{ + /*implicit*/ ItemBonusGenerationParams(ItemContext context, Optional<int32> mythicPlusKeystoneLevel = {}, Optional<int32> pvpTier = {}) + : Context(context), MythicPlusKeystoneLevel(mythicPlusKeystoneLevel), PvpTier(pvpTier) { } + + ItemContext Context; + Optional<int32> MythicPlusKeystoneLevel; + Optional<int32> PvpTier; +}; + +TC_GAME_API std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const& params); +TC_GAME_API std::vector<int32> GetAllBonusListsForTree(uint32 itemBonusTreeId); +} + +#endif // TRINITY_ITEM_BONUS_MGR_H diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp index 1c4f55c4c81..297d39f1da9 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp @@ -19,6 +19,7 @@ #include "Containers.h" #include "DatabaseEnv.h" #include "DB2Stores.h" +#include "ItemBonusMgr.h" #include "ItemTemplate.h" #include "Log.h" #include "ObjectMgr.h" @@ -58,7 +59,7 @@ void LoadItemRandomBonusListTemplates() uint32 bonusListId = fields[1].GetUInt32(); float chance = fields[2].GetFloat(); - if (!sDB2Manager.GetItemBonusList(bonusListId)) + 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); continue; diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 17bab58f10d..2c48fbdb50a 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -298,7 +298,15 @@ enum ItemFlags4 ITEM_FLAG4_SQUISH_USING_ITEM_LEVEL_AS_PLAYER_LEVEL = 0x00004000, ITEM_FLAG4_ALWAYS_SHOW_SELL_PRICE_IN_TOOLTIP = 0x00008000, ITEM_FLAG4_COSMETIC_ITEM = 0x00010000, - ITEM_FLAG4_NO_SPELL_EFFECT_TOOLTIP_PREFIXES = 0x00020000 + ITEM_FLAG4_NO_SPELL_EFFECT_TOOLTIP_PREFIXES = 0x00020000, + ITEM_FLAG4_IGNORE_COSMETIC_COLLECTION_BEHAVIOR = 0x00040000, + ITEM_FLAG4_NPC_ONLY = 0x00080000, + ITEM_FLAG4_NOT_RESTORABLE = 0x00100000, + ITEM_FLAG4_DONT_DISPLAY_AS_CRAFTING_REAGENT = 0x00200000, + ITEM_FLAG4_DISPLAY_REAGENT_QUALITY_AS_CRAFTED_QUALITY = 0x00400000, + ITEM_FLAG4_NO_SALVAGE = 0x00800000, + ITEM_FLAG4_RECRAFTABLE = 0x01000000, + ITEM_FLAG4_CC_TRINKET = 0x02000000, }; enum ItemFlagsCustom diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 7b7ad04704b..0ba8f8e4890 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -920,7 +920,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) ApplyLevelScaling(); CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, creatureDifficulty->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * creatureDifficulty->HealthModifier * _GetHealthMod(cinfo->rank), 1.0f)); + SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, creatureDifficulty->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class), 0) * creatureDifficulty->HealthModifier * _GetHealthMod(cinfo->rank), 1.0f)); SetCreateMana(stats->BaseMana); SetCreateStat(STAT_STRENGTH, 22); SetCreateStat(STAT_AGILITY, 22); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 61734358212..feb7f877ce6 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -587,10 +587,6 @@ bool Player::StoreNewItemInBestSlots(uint32 itemId, uint32 amount, ItemContext c TC_LOG_DEBUG("entities.player.items", "Player::StoreNewItemInBestSlots: Player '{}' ({}) creates initial item (ItemID: {}, Count: {})", GetName(), GetGUID().ToString(), itemId, amount); - std::vector<int32> bonusListIDs; - std::set<uint32> contextBonuses = sDB2Manager.GetDefaultItemBonusTree(itemId, context); - bonusListIDs.insert(bonusListIDs.begin(), contextBonuses.begin(), contextBonuses.end()); - // attempt equip by one while (amount > 0) { @@ -599,8 +595,7 @@ bool Player::StoreNewItemInBestSlots(uint32 itemId, uint32 amount, ItemContext c if (msg != EQUIP_ERR_OK) break; - Item* item = EquipNewItem(eDest, itemId, context, true); - item->SetBonuses(bonusListIDs); + EquipNewItem(eDest, itemId, context, true); AutoUnequipOffhandIfNeed(); --amount; } @@ -614,7 +609,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, bonusListIDs); + StoreNewItem(sDest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), context); return true; // stored } @@ -11450,18 +11445,20 @@ 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*/, - GuidSet const& allowedLooters /*= GuidSet()*/, ItemContext context /*= ItemContext::NONE*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/, bool addToCollection /*= true*/) + GuidSet const& allowedLooters /*= GuidSet()*/, ItemContext context /*= ItemContext::NONE*/, + std::vector<int32> const* bonusListIDs /*= std::vector<int32>()*/, bool addToCollection /*= true*/) { uint32 count = 0; for (ItemPosCountVec::const_iterator itr = pos.begin(); itr != pos.end(); ++itr) count += itr->count; - Item* item = Item::CreateItem(itemId, count, context, this); + Item* item = Item::CreateItem(itemId, count, context, this, bonusListIDs == nullptr); if (item) { item->SetItemFlag(ITEM_FIELD_FLAG_NEW_ITEM); - item->SetBonuses(bonusListIDs); + if (bonusListIDs) + item->SetBonuses(*bonusListIDs); item = StoreItem(pos, item, update); @@ -15021,7 +15018,7 @@ bool Player::CanSelectQuestPackageItem(QuestPackageItemEntry const* questPackage return false; } -void Player::RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId /*= 0*/) +void Player::RewardQuestPackage(uint32 questPackageId, ItemContext context, uint32 onlyItemId /*= 0*/) { bool hasFilteredQuestPackageReward = false; if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(questPackageId)) @@ -15037,7 +15034,7 @@ void Player::RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId /*= 0*/ 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)); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomBonusListId(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -15056,7 +15053,7 @@ void Player::RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId /*= 0*/ 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)); + Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomBonusListId(questPackageItem->ItemID), {}, context); SendNewItem(item, questPackageItem->ItemQuantity, true, false); } } @@ -15116,7 +15113,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)); + Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardItemCount[i], true, false); } else if (quest->IsDFQuest()) @@ -15161,7 +15158,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)); + Item* item = StoreNewItem(dest, rewardId, true, GenerateItemRandomBonusListId(rewardId), {}, ItemContext::Quest_Reward); SendNewItem(item, quest->RewardChoiceItemCount[i], true, false); } } @@ -15170,7 +15167,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew // QuestPackageItem.db2 if (rewardProto && quest->GetQuestPackageID()) - RewardQuestPackage(quest->GetQuestPackageID(), rewardId); + RewardQuestPackage(quest->GetQuestPackageID(), ItemContext::Quest_Reward, rewardId); break; } case LootItemType::Currency: @@ -22511,7 +22508,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), {}, ItemContext::Vendor, &crItem->BonusListIDs, false) : EquipNewItem(uiDest, item, ItemContext::Vendor, true); if (it) { @@ -25634,7 +25631,7 @@ void Player::AtExitCombat() float Player::GetBlockPercent(uint8 attackerLevel) const { float blockArmor = float(*m_activePlayerData->ShieldBlock); - float armorConstant = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, attackerLevel, -2, 0, CLASS_NONE); + float armorConstant = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, attackerLevel, -2, 0, CLASS_NONE, 0); if (!(blockArmor + armorConstant)) return 0; @@ -25969,7 +25966,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->randomBonusListId, item->GetAllowedLooters(), item->context, item->BonusListIDs); + Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomBonusListId, item->GetAllowedLooters(), item->context); if (ffaItem) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 71b00fe46d9..297bad68b3f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1434,7 +1434,8 @@ 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, ItemRandomBonusListId randomBonusListId = 0, GuidSet const& allowedLooters = GuidSet(), ItemContext context = ItemContext::NONE, std::vector<int32> const& bonusListIDs = std::vector<int32>(), bool addToCollection = true); + Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, ItemRandomBonusListId randomBonusListId = 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); Item* EquipItem(uint16 pos, Item* pItem, bool update); @@ -1585,7 +1586,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 GetQuestMoneyReward(Quest const* quest) const; uint32 GetQuestXPReward(Quest const* quest); bool CanSelectQuestPackageItem(QuestPackageItemEntry const* questPackageItem) const; - void RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId = 0); + void RewardQuestPackage(uint32 questPackageId, ItemContext context, uint32 onlyItemId = 0); void RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rewardId, Object* questGiver, bool announce = true); void SetRewardedQuest(uint32 quest_id); void FailQuest(uint32 quest_id); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 9085adbd570..c08291e16a4 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -45,6 +45,7 @@ #include "Group.h" #include "InstanceScript.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "KillRewarder.h" #include "Log.h" #include "Loot.h" @@ -1673,7 +1674,7 @@ void Unit::HandleEmoteCommand(Emote emoteId, Player* target /*=nullptr*/, Trinit } // Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ? - float armorConstant = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, attackerLevel, -2, 0, attackerClass); + float armorConstant = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, attackerLevel, -2, 0, attackerClass, 0); if (!(armor + armorConstant)) return damage; @@ -10755,7 +10756,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) { creature->m_personalLoot = GenerateDungeonEncounterPersonalLoot(dungeonEncounter->ID, creature->GetLootId(), LootTemplates_Creature, LOOT_CORPSE, creature, creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax, - creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext(), tappers); + creature->GetLootMode(), creature->GetMap()->GetMapDifficulty(), tappers); } else if (!tappers.empty()) { @@ -10765,7 +10766,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_CORPSE, dungeonEncounter ? group : nullptr); if (uint32 lootid = creature->GetLootId()) - loot->FillLoot(lootid, LootTemplates_Creature, looter, dungeonEncounter != nullptr, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext()); + loot->FillLoot(lootid, LootTemplates_Creature, looter, dungeonEncounter != nullptr, false, creature->GetLootMode(), ItemBonusMgr::GetContextForPlayer(creature->GetMap()->GetMapDifficulty(), looter)); if (creature->GetLootMode() > 0) loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax); @@ -10791,7 +10792,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) loot->SetDungeonEncounterId(dungeonEncounter->ID); if (uint32 lootid = creature->GetLootId()) - loot->FillLoot(lootid, LootTemplates_Creature, tapper, true, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext()); + loot->FillLoot(lootid, LootTemplates_Creature, tapper, true, false, creature->GetLootMode(), ItemBonusMgr::GetContextForPlayer(creature->GetMap()->GetMapDifficulty(), tapper)); if (creature->GetLootMode() > 0) loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index fb004f8b6eb..4228576ada1 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -37,6 +37,7 @@ #include "GuildMgr.h" #include "InstanceScript.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "Language.h" #include "LFGMgr.h" #include "Log.h" @@ -9853,11 +9854,11 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, return false; } - for (int32 bonusList : vItem.BonusListIDs) + for (int32 bonusListId : vItem.BonusListIDs) { - if (!sDB2Manager.GetItemBonusList(bonusList)) + if (ItemBonusMgr::GetItemBonuses(bonusListId).empty()) { - TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}) with invalid bonus {} for vendor ({}), ignore", vItem.item, bonusList, vendor_entry); + TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}) with invalid bonus {} for vendor ({}), ignore", vItem.item, bonusListId, vendor_entry); return false; } } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 0e975838e32..998f5d09ce4 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -460,7 +460,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.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/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index e7773549434..9d97c66e4a8 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -598,12 +598,9 @@ void WorldSession::HandleMailCreateTextItem(WorldPackets::Mail::MailCreateTextIt return; } - Item* bodyItem = new Item; // This is not bag and then can be used new Item. - if (!bodyItem->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), MAIL_BODY_ITEM_TEMPLATE, ItemContext::NONE, player)) - { - delete bodyItem; + Item* bodyItem = Item::CreateItem(MAIL_BODY_ITEM_TEMPLATE, 1, ItemContext::NONE, player); + if (!bodyItem) return; - } // in mail template case we need create new item text if (m->mailTemplateId) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 1e376a55d90..e4d33fb37cd 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -809,7 +809,7 @@ void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceRespons _player->SetTitle(sCharTitlesStore.AssertEntry(playerChoiceResponse->Reward->TitleId), false); if (playerChoiceResponse->Reward->PackageId) - _player->RewardQuestPackage(playerChoiceResponse->Reward->PackageId); + _player->RewardQuestPackage(playerChoiceResponse->Reward->PackageId, ItemContext::NONE); if (playerChoiceResponse->Reward->SkillLineId && _player->HasSkill(playerChoiceResponse->Reward->SkillLineId)) _player->UpdateSkillPro(playerChoiceResponse->Reward->SkillLineId, 1000, playerChoiceResponse->Reward->SkillPointCount); @@ -828,7 +828,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, GenerateItemRandomBonusListId(item.Id), {}, ItemContext::Quest_Reward, item.BonusListIDs); + Item* newItem = _player->StoreNewItem(dest, item.Id, true, GenerateItemRandomBonusListId(item.Id), {}, ItemContext::Quest_Reward, &item.BonusListIDs); _player->SendNewItem(newItem, item.Quantity, true, false); } } diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp index e48f55ee3e3..cafa7070c3e 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 40e185463ec..ac75747d60a 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -22,6 +22,7 @@ #include "GameTime.h" #include "Group.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "ItemTemplate.h" #include "Log.h" #include "LootMgr.h" @@ -835,11 +836,7 @@ void Loot::AddItem(LootStoreItem const& item) generatedLoot.context = _itemContext; generatedLoot.count = std::min(count, proto->GetMaxStackSize()); generatedLoot.LootListId = items.size(); - if (_itemContext != ItemContext::NONE) - { - std::set<uint32> bonusListIDs = sDB2Manager.GetDefaultItemBonusTree(generatedLoot.itemid, _itemContext); - generatedLoot.BonusListIDs.insert(generatedLoot.BonusListIDs.end(), bonusListIDs.begin(), bonusListIDs.end()); - } + generatedLoot.BonusListIDs = ItemBonusMgr::GetBonusListsForItem(generatedLoot.itemid, _itemContext); items.push_back(generatedLoot); count -= proto->GetMaxStackSize(); @@ -888,7 +885,7 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool --unlootedCount; - Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, lootItem->BonusListIDs); + Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs); player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast); player->ApplyItemLootedSpell(pItem, true); } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 00959ce3a0b..dec8215dc50 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -19,6 +19,7 @@ #include "Containers.h" #include "DatabaseEnv.h" #include "DB2Stores.h" +#include "ItemBonusMgr.h" #include "ItemTemplate.h" #include "Log.h" #include "Loot.h" @@ -947,7 +948,8 @@ bool LootTemplate::isReference(uint32 id) } std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const& store, - LootType type, WorldObject const* lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, ItemContext context, std::vector<Player*> const& tappers) + LootType type, WorldObject const* lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, MapDifficultyEntry const* mapDifficulty, + std::vector<Player*> const& tappers) { std::unordered_map<Player*, std::unique_ptr<Loot>> tempLoot; @@ -958,7 +960,7 @@ std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> GenerateDungeonEncounterPe std::unique_ptr<Loot>& loot = tempLoot[tapper]; loot.reset(new Loot(lootOwner->GetMap(), lootOwner->GetGUID(), type, nullptr)); - loot->SetItemContext(context); + loot->SetItemContext(ItemBonusMgr::GetContextForPlayer(mapDifficulty, tapper)); loot->SetDungeonEncounterId(dungeonEncounterId); loot->generateMoneyLoot(minMoney, maxMoney); } diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 5d7b0cb031d..c481f4009ea 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -32,6 +32,7 @@ class LootTemplate; class Player; struct Loot; struct LootItem; +struct MapDifficultyEntry; enum LootType : uint8; enum class ItemContext : uint8; @@ -142,7 +143,7 @@ class TC_GAME_API LootTemplate std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const& store, LootType type, WorldObject const* lootOwner, uint32 minMoney, uint32 maxMoney, - uint16 lootMode, ItemContext context, + uint16 lootMode, MapDifficultyEntry const* mapDifficulty, std::vector<Player*> const& tappers); //===================================================== diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index a7736b13a0e..a64460e9bf9 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3173,18 +3173,6 @@ MapDifficultyEntry const* Map::GetMapDifficulty() const return sDB2Manager.GetMapDifficultyData(GetId(), GetDifficultyID()); } -ItemContext Map::GetDifficultyLootItemContext() const -{ - if (MapDifficultyEntry const* mapDifficulty = GetMapDifficulty()) - if (mapDifficulty->ItemContext) - return ItemContext(mapDifficulty->ItemContext); - - if (DifficultyEntry const* difficulty = sDifficultyStore.LookupEntry(GetDifficultyID())) - return ItemContext(difficulty->ItemContext); - - return ItemContext::NONE; -} - uint32 Map::GetId() const { return i_mapEntry->ID; diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index e0fcfabd056..3a570714176 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -306,7 +306,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType> // have meaning only for instanced map (that have set real difficulty) Difficulty GetDifficultyID() const { return Difficulty(i_spawnMode); } MapDifficultyEntry const* GetMapDifficulty() const; - ItemContext GetDifficultyLootItemContext() const; uint32 GetId() const; bool Instanceable() const; diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index eb84c26bcbf..38071b7c097 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -495,7 +495,7 @@ class TC_GAME_API Spell uint32 getState() const { return m_spellState; } void setState(uint32 state) { m_spellState = state; } - void DoCreateItem(uint32 itemId, ItemContext context = ItemContext::NONE, std::vector<int32> const& bonusListIDs = std::vector<int32>()); + void DoCreateItem(uint32 itemId, ItemContext context = ItemContext::NONE, std::vector<int32> const* bonusListIDs = nullptr); bool CheckEffectTarget(Unit const* target, SpellEffectInfo const& spellEffectInfo, Position const* losPosition) const; bool CheckEffectTarget(GameObject const* target, SpellEffectInfo const& spellEffectInfo) const; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index e9ffb75cac7..3ac7951aa29 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1271,7 +1271,7 @@ void Spell::EffectHealthLeech() } } -void Spell::DoCreateItem(uint32 itemId, ItemContext context /*= ItemContext::NONE*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/) +void Spell::DoCreateItem(uint32 itemId, ItemContext context /*= ItemContext::NONE*/, std::vector<int32> const* bonusListIDs /*= nullptr*/) { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; @@ -5383,7 +5383,7 @@ void Spell::EffectCreateHeirloomItem() std::vector<int32> bonusList; bonusList.push_back(collectionMgr->GetHeirloomBonus(m_misc.Raw.Data[0])); - DoCreateItem(m_misc.Raw.Data[0], ItemContext::NONE, bonusList); + DoCreateItem(m_misc.Raw.Data[0], ItemContext::NONE, &bonusList); ExecuteLogEffectCreateItem(SpellEffectName(effectInfo->Effect), m_misc.Raw.Data[0]); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index b22077fcba7..d80728ba8c5 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -633,7 +633,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ expansion = contentTuning->ExpansionID; int32 level = caster && caster->IsUnit() ? int32(caster->ToUnit()->GetLevel()) : 1; - value = sDB2Manager.EvaluateExpectedStat(stat, level, expansion, 0, CLASS_NONE) * BasePoints / 100.0f; + value = sDB2Manager.EvaluateExpectedStat(stat, level, expansion, 0, CLASS_NONE, 0) * BasePoints / 100.0f; } return int32(round(value)); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e4b0c88190e..0eae781138a 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -61,6 +61,7 @@ #include "GuildMgr.h" #include "InstanceLockMgr.h" #include "IPLocation.h" +#include "ItemBonusMgr.h" #include "Language.h" #include "LanguageMgr.h" #include "LFGMgr.h" @@ -1954,6 +1955,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Enchant Spells Proc datas..."); sSpellMgr->LoadSpellEnchantProcData(); + TC_LOG_INFO("server.loading", "Loading item bonus data..."); + ItemBonusMgr::Load(); + TC_LOG_INFO("server.loading", "Loading Random item bonus list definitions..."); LoadItemRandomBonusListTemplates(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 14cba07953b..efaf039149a 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -30,6 +30,7 @@ #include "IpAddress.h" #include "IPLocation.h" #include "Item.h" +#include "ItemBonusMgr.h" #include "Language.h" #include "MiscPackets.h" #include "MMapFactory.h" @@ -1227,16 +1228,16 @@ public: // semicolon separated bonuslist ids (parse them after all arguments are extracted by strtok!) if (bonuses) for (std::string_view token : Trinity::Tokenize(bonuses, ';', false)) - if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token)) + if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token); bonusListId && *bonusListId) bonusListIDs.push_back(*bonusListId); ItemContext itemContext = ItemContext::NONE; if (context) { itemContext = ItemContext(Trinity::StringTo<uint8>(context).value_or(0)); - if (itemContext != ItemContext::NONE && itemContext < ItemContext::Max) + if (itemContext < ItemContext::Max) { - std::set<uint32> contextBonuses = sDB2Manager.GetDefaultItemBonusTree(itemId, itemContext); + std::vector<int32> contextBonuses = ItemBonusMgr::GetBonusListsForItem(itemId, itemContext); bonusListIDs.insert(bonusListIDs.begin(), contextBonuses.begin(), contextBonuses.end()); } } @@ -1297,7 +1298,8 @@ public: return false; } - Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), itemContext, bonusListIDs); + Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), itemContext, + bonusListIDs.empty() ? nullptr : &bonusListIDs); // remove binding (let GM give it to another player later) if (player == playerTarget) @@ -1392,16 +1394,16 @@ public: // semicolon separated bonuslist ids (parse them after all arguments are extracted by strtok!) if (bonuses) for (std::string_view token : Trinity::Tokenize(bonuses, ';', false)) - if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token)) + if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token); bonusListId && *bonusListId) bonusListIDs.push_back(*bonusListId); ItemContext itemContext = ItemContext::NONE; if (context) { itemContext = ItemContext(Trinity::StringTo<uint8>(context).value_or(0)); - if (itemContext != ItemContext::NONE && itemContext < ItemContext::Max) + if (itemContext < ItemContext::Max) { - std::set<uint32> contextBonuses = sDB2Manager.GetDefaultItemBonusTree(itemId, itemContext); + std::vector<int32> contextBonuses = ItemBonusMgr::GetBonusListsForItem(itemId, itemContext); bonusListIDs.insert(bonusListIDs.begin(), contextBonuses.begin(), contextBonuses.end()); } } @@ -1457,7 +1459,8 @@ public: return false; } - Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), itemContext, bonusListIDs); + Item* item = playerTarget->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), itemContext, + bonusListIDs.empty() ? nullptr : &bonusListIDs); // remove binding (let GM give it to another player later) if (player == playerTarget) @@ -1493,7 +1496,7 @@ public: // semicolon separated bonuslist ids (parse them after all arguments are extracted by strtok!) if (bonuses) for (std::string_view token : Trinity::Tokenize(*bonuses, ';', false)) - if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token)) + if (Optional<int32> bonusListId = Trinity::StringTo<int32>(token); bonusListId && *bonusListId) bonusListIDs.push_back(*bonusListId); ItemContext itemContext = ItemContext::NONE; @@ -1518,13 +1521,14 @@ public: if (msg == EQUIP_ERR_OK) { std::vector<int32> bonusListIDsForItem = bonusListIDs; // copy, bonuses for each depending on context might be different for each item - if (itemContext != ItemContext::NONE && itemContext < ItemContext::Max) + if (itemContext < ItemContext::Max) { - std::set<uint32> contextBonuses = sDB2Manager.GetDefaultItemBonusTree(itemTemplatePair.first, itemContext); + std::vector<int32> contextBonuses = ItemBonusMgr::GetBonusListsForItem(itemTemplatePair.first, itemContext); bonusListIDsForItem.insert(bonusListIDsForItem.begin(), contextBonuses.begin(), contextBonuses.end()); } - Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, {}, GuidSet(), itemContext, bonusListIDsForItem); + Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, {}, GuidSet(), itemContext, + bonusListIDsForItem.empty() ? nullptr : &bonusListIDsForItem); // remove binding (let GM give it to another player later) if (player == playerTarget) diff --git a/src/server/scripts/Spells/spell_monk.cpp b/src/server/scripts/Spells/spell_monk.cpp index f74034b8d21..d5846f9b16d 100644 --- a/src/server/scripts/Spells/spell_monk.cpp +++ b/src/server/scripts/Spells/spell_monk.cpp @@ -341,7 +341,7 @@ class spell_monk_stagger : public AuraScript Unit* target = GetTarget(); float agility = target->GetStat(STAT_AGILITY); float base = CalculatePct(agility, float(effect->GetAmount())); - float K = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, target->GetLevel(), -2, 0, Classes(target->GetClass())); + float K = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, target->GetLevel(), -2, 0, Classes(target->GetClass()), 0); float newAmount = (base / (base + K)); newAmount *= multiplier; |