aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/hotfixes/master/2023_06_17_00_hotfixes.sql119
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp41
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h24
-rw-r--r--src/server/game/Achievements/CriteriaHandler.cpp5
-rw-r--r--src/server/game/Chat/HyperlinkTags.cpp15
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h120
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp305
-rw-r--r--src/server/game/DataStores/DB2Stores.h25
-rw-r--r--src/server/game/DataStores/DB2Structure.h91
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp6
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp23
-rw-r--r--src/server/game/Entities/Item/Item.cpp49
-rw-r--r--src/server/game/Entities/Item/Item.h2
-rw-r--r--src/server/game/Entities/Item/ItemBonusMgr.cpp509
-rw-r--r--src/server/game/Entities/Item/ItemBonusMgr.h55
-rw-r--r--src/server/game/Entities/Item/ItemEnchantmentMgr.cpp3
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h10
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp35
-rw-r--r--src/server/game/Entities/Player/Player.h5
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp9
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp7
-rw-r--r--src/server/game/Handlers/LootHandler.cpp2
-rw-r--r--src/server/game/Handlers/MailHandler.cpp7
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp4
-rw-r--r--src/server/game/Handlers/VoidStorageHandler.cpp2
-rw-r--r--src/server/game/Loot/Loot.cpp9
-rw-r--r--src/server/game/Loot/LootMgr.cpp6
-rw-r--r--src/server/game/Loot/LootMgr.h3
-rw-r--r--src/server/game/Maps/Map.cpp12
-rw-r--r--src/server/game/Maps/Map.h1
-rw-r--r--src/server/game/Spells/Spell.h2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp4
-rw-r--r--src/server/game/Spells/SpellInfo.cpp2
-rw-r--r--src/server/game/World/World.cpp4
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp28
-rw-r--r--src/server/scripts/Spells/spell_monk.cpp2
37 files changed, 1168 insertions, 380 deletions
diff --git a/sql/updates/hotfixes/master/2023_06_17_00_hotfixes.sql b/sql/updates/hotfixes/master/2023_06_17_00_hotfixes.sql
new file mode 100644
index 00000000000..45783760d1f
--- /dev/null
+++ b/sql/updates/hotfixes/master/2023_06_17_00_hotfixes.sql
@@ -0,0 +1,119 @@
+--
+-- Table structure for table `challenge_mode_item_bonus_override`
+--
+DROP TABLE IF EXISTS `challenge_mode_item_bonus_override`;
+CREATE TABLE `challenge_mode_item_bonus_override` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemBonusTreeGroupID` int(11) NOT NULL DEFAULT '0',
+ `DstItemBonusTreeID` int(11) NOT NULL DEFAULT '0',
+ `Type` tinyint(4) NOT NULL DEFAULT '0',
+ `Value` int(11) NOT NULL DEFAULT '0',
+ `MythicPlusSeasonID` int(11) NOT NULL DEFAULT '0',
+ `PvPSeasonID` int(11) NOT NULL DEFAULT '0',
+ `SrcItemBonusTreeID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `conditional_content_tuning`
+--
+DROP TABLE IF EXISTS `conditional_content_tuning`;
+CREATE TABLE `conditional_content_tuning` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `OrderIndex` int(11) NOT NULL DEFAULT '0',
+ `RedirectContentTuningID` int(11) NOT NULL DEFAULT '0',
+ `RedirectFlag` int(11) NOT NULL DEFAULT '0',
+ `ParentContentTuningID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `content_tuning_x_label`
+--
+DROP TABLE IF EXISTS `content_tuning_x_label`;
+CREATE TABLE `content_tuning_x_label` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `LabelID` int(11) NOT NULL DEFAULT '0',
+ `ContentTuningID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `item_bonus_list_group_entry`
+--
+DROP TABLE IF EXISTS `item_bonus_list_group_entry`;
+CREATE TABLE `item_bonus_list_group_entry` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemBonusListGroupID` int(11) NOT NULL DEFAULT '0',
+ `ItemBonusListID` int(11) NOT NULL DEFAULT '0',
+ `ItemLevelSelectorID` int(11) NOT NULL DEFAULT '0',
+ `SequenceValue` int(11) NOT NULL DEFAULT '0',
+ `ItemExtendedCostID` int(11) NOT NULL DEFAULT '0',
+ `PlayerConditionID` int(11) NOT NULL DEFAULT '0',
+ `Flags` int(11) NOT NULL DEFAULT '0',
+ `ItemLogicalCostGroupID` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `item_bonus_tree`
+--
+DROP TABLE IF EXISTS `item_bonus_tree`;
+CREATE TABLE `item_bonus_tree` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `Flags` int(11) NOT NULL DEFAULT '0',
+ `InventoryTypeSlotMask` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+ALTER TABLE `item_bonus_tree_node` CHANGE `Unknown1010_1` `MinMythicPlusLevel` int(11) NOT NULL DEFAULT '0';
+ALTER TABLE `item_bonus_tree_node` CHANGE `Unknown1010_2` `MaxMythicPlusLevel` int(11) NOT NULL DEFAULT '0';
+
+--
+-- Table structure for table `item_context_picker_entry`
+--
+DROP TABLE IF EXISTS `item_context_picker_entry`;
+CREATE TABLE `item_context_picker_entry` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemCreationContext` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `OrderIndex` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `PVal` int(11) NOT NULL DEFAULT '0',
+ `LabelID` int(11) NOT NULL DEFAULT '0',
+ `Flags` int(10) unsigned NOT NULL DEFAULT '0',
+ `PlayerConditionID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemContextPickerID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `mythic_plus_season`
+--
+DROP TABLE IF EXISTS `mythic_plus_season`;
+CREATE TABLE `mythic_plus_season` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `MilestoneSeason` int(11) NOT NULL DEFAULT '0',
+ `ExpansionLevel` int(11) NOT NULL DEFAULT '0',
+ `HeroicLFGDungeonMinGear` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `pvp_season`
+--
+DROP TABLE IF EXISTS `pvp_season`;
+CREATE TABLE `pvp_season` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `MilestoneSeason` int(11) NOT NULL DEFAULT '0',
+ `AllianceAchievementID` int(11) NOT NULL DEFAULT '0',
+ `HordeAchievementID` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
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;