aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-12-05 00:12:35 +0100
committerShauren <shauren.trinity@gmail.com>2019-12-05 00:12:35 +0100
commitd934824421c83598853487c5cc9e4cbb3c5d0006 (patch)
treea63415c891a43c417747edeae37ac15ecb4a2389
parent65b91375e0a3b01eaacab77f973429b544c63a61 (diff)
Core/Items: Implement azerite empowered items
-rw-r--r--sql/base/characters_database.sql31
-rw-r--r--sql/updates/characters/master/2019_12_05_00_characters.sql15
-rw-r--r--sql/updates/hotfixes/master/2019_12_05_00_hotfixes.sql67
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp10
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h6
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp19
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h10
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h83
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp113
-rw-r--r--src/server/game/DataStores/DB2Stores.h3
-rw-r--r--src/server/game/DataStores/DB2Structure.h43
-rw-r--r--src/server/game/DataStores/DBCEnums.h11
-rw-r--r--src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.cpp192
-rw-r--r--src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.h57
-rw-r--r--src/server/game/Entities/Item/Item.cpp56
-rw-r--r--src/server/game/Entities/Item/Item.h19
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h4
-rw-r--r--src/server/game/Entities/Player/Player.cpp87
-rw-r--r--src/server/game/Entities/Player/Player.h10
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp2
-rw-r--r--src/server/game/Handlers/AzeriteHandler.cpp65
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp4
-rw-r--r--src/server/game/Server/Packets/AzeritePackets.cpp28
-rw-r--r--src/server/game/Server/Packets/AzeritePackets.h43
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp8
-rw-r--r--src/server/game/Server/WorldSession.h4
-rw-r--r--src/server/game/Spells/Spell.cpp32
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp31
-rw-r--r--src/server/scripts/Spells/spell_item.cpp19
30 files changed, 1036 insertions, 37 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index ee50bbdf9c3..67f7d55312e 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -1694,6 +1694,7 @@ CREATE TABLE `characters` (
`rest_bonus` float NOT NULL DEFAULT '0',
`resettalents_cost` int(10) unsigned NOT NULL DEFAULT '0',
`resettalents_time` int(10) unsigned NOT NULL DEFAULT '0',
+ `numRespecs` tinyint(3) unsigned NOT NULL DEFAULT '0',
`primarySpecialization` int(10) unsigned NOT NULL DEFAULT '0',
`trans_x` float NOT NULL DEFAULT '0',
`trans_y` float NOT NULL DEFAULT '0',
@@ -2898,6 +2899,33 @@ LOCK TABLES `item_instance_azerite` WRITE;
UNLOCK TABLES;
--
+-- Table structure for table `item_instance_azerite_empowered`
+--
+
+DROP TABLE IF EXISTS `item_instance_azerite_empowered`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `item_instance_azerite_empowered` (
+ `itemGuid` bigint(20) unsigned NOT NULL,
+ `azeritePowerId1` int(11) NOT NULL,
+ `azeritePowerId2` int(11) NOT NULL,
+ `azeritePowerId3` int(11) NOT NULL,
+ `azeritePowerId4` int(11) NOT NULL,
+ `azeritePowerId5` int(11) NOT NULL,
+ PRIMARY KEY (`itemGuid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `item_instance_azerite_empowered`
+--
+
+LOCK TABLES `item_instance_azerite_empowered` WRITE;
+/*!40000 ALTER TABLE `item_instance_azerite_empowered` DISABLE KEYS */;
+/*!40000 ALTER TABLE `item_instance_azerite_empowered` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `item_instance_azerite_milestone_power`
--
@@ -3662,7 +3690,8 @@ INSERT INTO `updates` VALUES
('2019_11_03_00_characters.sql','DC789597F85B890E9A7901B4443DAD9CAEE2A02A','RELEASED','2019-11-03 14:13:27',0),
('2019_11_12_00_characters.sql','D4C642B4D48DAE9F56329BDE51C258323A132A91','RELEASED','2019-11-12 16:31:29',0),
('2019_11_22_00_characters.sql','95DFA71DBD75542C098CD86E9C0051C9690902F0','RELEASED','2019-11-20 15:10:12',0),
-('2019_11_30_00_characters.sql','D0678E62B651AECA60C2DD6989BF80BD999AD12B','RELEASED','2019-11-29 22:42:01',0);
+('2019_11_30_00_characters.sql','D0678E62B651AECA60C2DD6989BF80BD999AD12B','RELEASED','2019-11-29 22:42:01',0),
+('2019_12_05_00_characters.sql','EA381C9634A5646A3168F15DF4E06A708A622762','RELEASED','2019-12-05 20:56:58',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/sql/updates/characters/master/2019_12_05_00_characters.sql b/sql/updates/characters/master/2019_12_05_00_characters.sql
new file mode 100644
index 00000000000..647ce02ec86
--- /dev/null
+++ b/sql/updates/characters/master/2019_12_05_00_characters.sql
@@ -0,0 +1,15 @@
+ALTER TABLE `characters` ADD `numRespecs` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `resettalents_time`;
+
+--
+-- Table structure for table `item_instance_azerite_empowered`
+--
+DROP TABLE IF EXISTS `item_instance_azerite_empowered`;
+CREATE TABLE `item_instance_azerite_empowered` (
+ `itemGuid` bigint(20) unsigned NOT NULL,
+ `azeritePowerId1` int(11) NOT NULL,
+ `azeritePowerId2` int(11) NOT NULL,
+ `azeritePowerId3` int(11) NOT NULL,
+ `azeritePowerId4` int(11) NOT NULL,
+ `azeritePowerId5` int(11) NOT NULL,
+ PRIMARY KEY (`itemGuid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/sql/updates/hotfixes/master/2019_12_05_00_hotfixes.sql b/sql/updates/hotfixes/master/2019_12_05_00_hotfixes.sql
new file mode 100644
index 00000000000..b7418b39c07
--- /dev/null
+++ b/sql/updates/hotfixes/master/2019_12_05_00_hotfixes.sql
@@ -0,0 +1,67 @@
+--
+-- Table structure for table `azerite_empowered_item`
+--
+DROP TABLE IF EXISTS `azerite_empowered_item`;
+CREATE TABLE `azerite_empowered_item` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemID` int(11) NOT NULL DEFAULT '0',
+ `AzeriteTierUnlockSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `AzeritePowerSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `azerite_power_set_member`
+--
+DROP TABLE IF EXISTS `azerite_power_set_member`;
+CREATE TABLE `azerite_power_set_member` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `AzeritePowerSetID` int(11) NOT NULL DEFAULT '0',
+ `AzeritePowerID` int(11) NOT NULL DEFAULT '0',
+ `Class` int(11) NOT NULL DEFAULT '0',
+ `Tier` int(11) NOT NULL DEFAULT '0',
+ `OrderIndex` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `azerite_tier_unlock`
+--
+DROP TABLE IF EXISTS `azerite_tier_unlock`;
+CREATE TABLE `azerite_tier_unlock` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemCreationContext` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `Tier` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `AzeriteLevel` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `AzeriteTierUnlockSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `azerite_tier_unlock_set`
+--
+DROP TABLE IF EXISTS `azerite_tier_unlock_set`;
+CREATE TABLE `azerite_tier_unlock_set` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `Flags` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `azerite_unlock_mapping`
+--
+DROP TABLE IF EXISTS `azerite_unlock_mapping`;
+CREATE TABLE `azerite_unlock_mapping` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ItemLevel` int(11) NOT NULL DEFAULT '0',
+ `ItemBonusListHead` int(11) NOT NULL DEFAULT '0',
+ `ItemBonusListShoulders` int(11) NOT NULL DEFAULT '0',
+ `ItemBonusListChest` int(11) NOT NULL DEFAULT '0',
+ `AzeriteUnlockMappingSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 7b65d86b9da..ad9460c2b23 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -81,7 +81,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"resettalents_time, primarySpecialization, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, "
"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
"health, power1, power2, power3, power4, power5, power6, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, raidDifficulty, legacyRaidDifficulty, fishingSteps, "
- "honor, honorLevel, honorRestState, honorRestBonus "
+ "honor, honorLevel, honorRestState, honorRestBonus, numRespecs "
"FROM characters c LEFT JOIN character_fishingsteps cfs ON c.guid = cfs.guid WHERE c.guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH);
@@ -154,6 +154,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"FROM item_instance_azerite iz INNER JOIN mail_items mi ON iz.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAILITEMS_AZERITE_MILESTONE_POWER, "SELECT iamp.itemGuid, iamp.azeriteItemMilestonePowerId FROM item_instance_azerite_milestone_power iamp INNER JOIN mail_items mi ON iamp.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAILITEMS_AZERITE_UNLOCKED_ESSENCE, "SELECT iaue.itemGuid, iaue.azeriteEssenceId, iaue.`rank` FROM item_instance_azerite_unlocked_essence iaue INNER JOIN mail_items mi ON iaue.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_MAILITEMS_AZERITE_EMPOWERED, "SELECT iae.itemGuid, iae.azeritePowerId1, iae.azeritePowerId2, iae.azeritePowerId3, iae.azeritePowerId4, iae.azeritePowerId5 FROM item_instance_azerite_empowered iae INNER JOIN mail_items mi ON iae.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT " SelectItemInstanceContent " FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, auctioneerguid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH);
PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, auctioneerguid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
@@ -218,6 +219,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE, "INSERT INTO item_instance_azerite_unlocked_essence (itemGuid, azeriteEssenceId, `rank`) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE, "DELETE FROM item_instance_azerite_unlocked_essence WHERE itemGuid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE_BY_OWNER, "DELETE iaue FROM item_instance_azerite_unlocked_essence iaue LEFT JOIN item_instance ii ON iaue.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_EMPOWERED, "SELECT iae.itemGuid, iae.azeritePowerId1, iae.azeritePowerId2, iae.azeritePowerId3, iae.azeritePowerId4, iae.azeritePowerId5 FROM item_instance_azerite_empowered iae INNER JOIN character_inventory ci ON iae.itemGuid = ci.item WHERE ci.guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_EMPOWERED, "INSERT INTO item_instance_azerite_empowered (itemGuid, azeritePowerId1, azeritePowerId2, azeritePowerId3, azeritePowerId4, azeritePowerId5) VALUES (?, ?, ?, ? ,? ,?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ITEM_INSTANCE_AZERITE_EMPOWERED, "UPDATE item_instance_azerite_empowered SET azeritePowerId1 = ?, azeritePowerId2 = ?, azeritePowerId3 = ?, azeritePowerId4 = ?, azeritePowerId5 = ? WHERE itemGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED, "DELETE FROM item_instance_azerite_empowered WHERE itemGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED_BY_OWNER, "DELETE iae FROM item_instance_azerite_empowered iae LEFT JOIN item_instance ii ON iae.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GIFT_OWNER, "UPDATE character_gifts SET guid = ? WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC);
@@ -458,7 +464,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,customDisplay1=?,customDisplay2=?,customDisplay3=?,inventorySlots=?,bankSlots=?,restState=?,playerFlags=?,playerFlagsEx=?,"
"map=?,instance_id=?,dungeonDifficulty=?,raidDifficulty=?,legacyRaidDifficulty=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?,"
- "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,primarySpecialization=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
+ "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,numRespecs=?,primarySpecialization=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
"totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,"
"watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,latency=?,activeTalentGroup=?,lootSpecId=?,exploredZones=?,"
"equipmentCache=?,knownTitles=?,actionBars=?,online=?,honor=?,honorLevel=?,honorRestState=?,honorRestBonus=?,lastLoginBuild=? WHERE guid=?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index cd3750702a6..9b1b309a2d0 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -120,6 +120,7 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_MAILITEMS_AZERITE,
CHAR_SEL_MAILITEMS_AZERITE_MILESTONE_POWER,
CHAR_SEL_MAILITEMS_AZERITE_UNLOCKED_ESSENCE,
+ CHAR_SEL_MAILITEMS_AZERITE_EMPOWERED,
CHAR_SEL_AUCTION_ITEMS,
CHAR_INS_AUCTION,
CHAR_DEL_AUCTION,
@@ -175,6 +176,11 @@ enum CharacterDatabaseStatements : uint32
CHAR_INS_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE,
CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE,
CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE_BY_OWNER,
+ CHAR_SEL_ITEM_INSTANCE_AZERITE_EMPOWERED,
+ CHAR_INS_ITEM_INSTANCE_AZERITE_EMPOWERED,
+ CHAR_UPD_ITEM_INSTANCE_AZERITE_EMPOWERED,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED_BY_OWNER,
CHAR_UPD_GIFT_OWNER,
CHAR_DEL_GIFT,
CHAR_SEL_CHARACTER_GIFT_BY_ITEM,
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index fd3c2d06888..f78c04b60a7 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -110,6 +110,10 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PrepareStatement(HOTFIX_SEL_AUCTION_HOUSE, "SELECT ID, Name, FactionID, DepositRate, ConsignmentRate FROM auction_house ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_AUCTION_HOUSE, "SELECT ID, Name_lang FROM auction_house_locale WHERE locale = ?", CONNECTION_SYNCH);
+ // AzeriteEmpoweredItem.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_EMPOWERED_ITEM, "SELECT ID, ItemID, AzeriteTierUnlockSetID, AzeritePowerSetID FROM azerite_empowered_item"
+ " ORDER BY ID DESC", CONNECTION_SYNCH);
+
// AzeriteEssence.db2
PrepareStatement(HOTFIX_SEL_AZERITE_ESSENCE, "SELECT Name, Description, ID, SpecSetID FROM azerite_essence ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_AZERITE_ESSENCE, "SELECT ID, Name_lang, Description_lang FROM azerite_essence_locale WHERE locale = ?", CONNECTION_SYNCH);
@@ -137,6 +141,21 @@ void HotfixDatabaseConnection::DoPrepareStatements()
// AzeritePower.db2
PrepareStatement(HOTFIX_SEL_AZERITE_POWER, "SELECT ID, SpellID, ItemBonusListID, SpecSetID, Flags FROM azerite_power ORDER BY ID DESC", CONNECTION_SYNCH);
+ // AzeritePowerSetMember.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_POWER_SET_MEMBER, "SELECT ID, AzeritePowerSetID, AzeritePowerID, Class, Tier, OrderIndex"
+ " FROM azerite_power_set_member ORDER BY ID DESC", CONNECTION_SYNCH);
+
+ // AzeriteTierUnlock.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_TIER_UNLOCK, "SELECT ID, ItemCreationContext, Tier, AzeriteLevel, AzeriteTierUnlockSetID"
+ " FROM azerite_tier_unlock ORDER BY ID DESC", CONNECTION_SYNCH);
+
+ // AzeriteTierUnlockSet.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_TIER_UNLOCK_SET, "SELECT ID, Flags FROM azerite_tier_unlock_set ORDER BY ID DESC", CONNECTION_SYNCH);
+
+ // AzeriteUnlockMapping.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_UNLOCK_MAPPING, "SELECT ID, ItemLevel, ItemBonusListHead, ItemBonusListShoulders, ItemBonusListChest, "
+ "AzeriteUnlockMappingSetID FROM azerite_unlock_mapping ORDER BY ID DESC", CONNECTION_SYNCH);
+
// BankBagSlotPrices.db2
PrepareStatement(HOTFIX_SEL_BANK_BAG_SLOT_PRICES, "SELECT ID, Cost FROM bank_bag_slot_prices ORDER BY ID DESC", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h
index ab5eea98c41..f67d433d500 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -75,6 +75,8 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_AUCTION_HOUSE,
HOTFIX_SEL_AUCTION_HOUSE_LOCALE,
+ HOTFIX_SEL_AZERITE_EMPOWERED_ITEM,
+
HOTFIX_SEL_AZERITE_ESSENCE,
HOTFIX_SEL_AZERITE_ESSENCE_LOCALE,
@@ -91,6 +93,14 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_AZERITE_POWER,
+ HOTFIX_SEL_AZERITE_POWER_SET_MEMBER,
+
+ HOTFIX_SEL_AZERITE_TIER_UNLOCK,
+
+ HOTFIX_SEL_AZERITE_TIER_UNLOCK_SET,
+
+ HOTFIX_SEL_AZERITE_UNLOCK_MAPPING,
+
HOTFIX_SEL_BANK_BAG_SLOT_PRICES,
HOTFIX_SEL_BANNED_ADDONS,
diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h
index 957e0d8c792..825780db6da 100644
--- a/src/server/game/DataStores/DB2LoadInfo.h
+++ b/src/server/game/DataStores/DB2LoadInfo.h
@@ -418,6 +418,22 @@ struct AuctionHouseLoadInfo
}
};
+struct AzeriteEmpoweredItemLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "ItemID" },
+ { false, FT_INT, "AzeriteTierUnlockSetID" },
+ { false, FT_INT, "AzeritePowerSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteEmpoweredItemMeta::Instance(), HOTFIX_SEL_AZERITE_EMPOWERED_ITEM);
+ return &loadInfo;
+ }
+};
+
struct AzeriteEssenceLoadInfo
{
static DB2LoadInfo const* Instance()
@@ -533,6 +549,73 @@ struct AzeritePowerLoadInfo
}
};
+struct AzeritePowerSetMemberLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "AzeritePowerSetID" },
+ { true, FT_INT, "AzeritePowerID" },
+ { true, FT_INT, "Class" },
+ { true, FT_INT, "Tier" },
+ { true, FT_INT, "OrderIndex" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeritePowerSetMemberMeta::Instance(), HOTFIX_SEL_AZERITE_POWER_SET_MEMBER);
+ return &loadInfo;
+ }
+};
+
+struct AzeriteTierUnlockLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_BYTE, "ItemCreationContext" },
+ { false, FT_BYTE, "Tier" },
+ { false, FT_BYTE, "AzeriteLevel" },
+ { false, FT_INT, "AzeriteTierUnlockSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteTierUnlockMeta::Instance(), HOTFIX_SEL_AZERITE_TIER_UNLOCK);
+ return &loadInfo;
+ }
+};
+
+struct AzeriteTierUnlockSetLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "Flags" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteTierUnlockSetMeta::Instance(), HOTFIX_SEL_AZERITE_TIER_UNLOCK_SET);
+ return &loadInfo;
+ }
+};
+
+struct AzeriteUnlockMappingLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "ItemLevel" },
+ { true, FT_INT, "ItemBonusListHead" },
+ { true, FT_INT, "ItemBonusListShoulders" },
+ { true, FT_INT, "ItemBonusListChest" },
+ { false, FT_INT, "AzeriteUnlockMappingSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteUnlockMappingMeta::Instance(), HOTFIX_SEL_AZERITE_UNLOCK_MAPPING);
+ return &loadInfo;
+ }
+};
+
struct BankBagSlotPricesLoadInfo
{
static DB2LoadInfo const* Instance()
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index d276b5e6aa3..14c5b089e4b 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -20,6 +20,7 @@
#include "DatabaseEnv.h"
#include "DB2LoadInfo.h"
#include "Hash.h"
+#include "ItemTemplate.h"
#include "IteratorPair.h"
#include "Log.h"
#include "ObjectDefines.h"
@@ -55,6 +56,7 @@ DB2Storage<ArtifactQuestXPEntry> sArtifactQuestXPStore("ArtifactQ
DB2Storage<ArtifactTierEntry> sArtifactTierStore("ArtifactTier.db2", ArtifactTierLoadInfo::Instance());
DB2Storage<ArtifactUnlockEntry> sArtifactUnlockStore("ArtifactUnlock.db2", ArtifactUnlockLoadInfo::Instance());
DB2Storage<AuctionHouseEntry> sAuctionHouseStore("AuctionHouse.db2", AuctionHouseLoadInfo::Instance());
+DB2Storage<AzeriteEmpoweredItemEntry> sAzeriteEmpoweredItemStore("AzeriteEmpoweredItem.db2", AzeriteEmpoweredItemLoadInfo::Instance());
DB2Storage<AzeriteEssenceEntry> sAzeriteEssenceStore("AzeriteEssence.db2", AzeriteEssenceLoadInfo::Instance());
DB2Storage<AzeriteEssencePowerEntry> sAzeriteEssencePowerStore("AzeriteEssencePower.db2", AzeriteEssencePowerLoadInfo::Instance());
DB2Storage<AzeriteItemEntry> sAzeriteItemStore("AzeriteItem.db2", AzeriteItemLoadInfo::Instance());
@@ -62,6 +64,10 @@ DB2Storage<AzeriteItemMilestonePowerEntry> sAzeriteItemMilestonePowerStore(
DB2Storage<AzeriteKnowledgeMultiplierEntry> sAzeriteKnowledgeMultiplierStore("AzeriteKnowledgeMultiplier.db2", AzeriteKnowledgeMultiplierLoadInfo::Instance());
DB2Storage<AzeriteLevelInfoEntry> sAzeriteLevelInfoStore("AzeriteLevelInfo.db2", AzeriteLevelInfoLoadInfo::Instance());
DB2Storage<AzeritePowerEntry> sAzeritePowerStore("AzeritePower.db2", AzeritePowerLoadInfo::Instance());
+DB2Storage<AzeritePowerSetMemberEntry> sAzeritePowerSetMemberStore("AzeritePowerSetMember.db2", AzeritePowerSetMemberLoadInfo::Instance());
+DB2Storage<AzeriteTierUnlockEntry> sAzeriteTierUnlockStore("AzeriteTierUnlock.db2", AzeriteTierUnlockLoadInfo::Instance());
+DB2Storage<AzeriteTierUnlockSetEntry> sAzeriteTierUnlockSetStore("AzeriteTierUnlockSet.db2", AzeriteTierUnlockSetLoadInfo::Instance());
+DB2Storage<AzeriteUnlockMappingEntry> sAzeriteUnlockMappingStore("AzeriteUnlockMapping.db2", AzeriteUnlockMappingLoadInfo::Instance());
DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore("BankBagSlotPrices.db2", BankBagSlotPricesLoadInfo::Instance());
DB2Storage<BannedAddonsEntry> sBannedAddonsStore("BannedAddons.db2", BannedAddonsLoadInfo::Instance());
DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore("BarberShopStyle.db2", BarberShopStyleLoadInfo::Instance());
@@ -299,6 +305,8 @@ struct ItemLevelSelectorQualityEntryComparator
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;
@@ -360,9 +368,13 @@ namespace
ArtifactPowersContainer _artifactPowers;
ArtifactPowerLinksContainer _artifactPowerLinks;
ArtifactPowerRanksContainer _artifactPowerRanks;
- std::unordered_map<std::pair<uint32, uint32>, AzeriteEssencePowerEntry const*> _azeriteEssencePowersByIdAndRank;
+ std::unordered_map<uint32 /*itemId*/, AzeriteEmpoweredItemEntry const*> _azeriteEmpoweredItems;
+ std::unordered_map<std::pair<uint32 /*azeriteEssenceId*/, uint32 /*rank*/>, AzeriteEssencePowerEntry const*> _azeriteEssencePowersByIdAndRank;
std::vector<AzeriteItemMilestonePowerEntry const*> _azeriteItemMilestonePowers;
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::set<std::tuple<uint8, uint8, uint32>> _characterFacialHairStyles;
std::multimap<std::tuple<uint8, uint8, CharBaseSectionVariation>, CharSectionsEntry const*> _charSections;
CharStartOutfitContainer _charStartOutfits;
@@ -532,6 +544,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sArtifactTierStore);
LOAD_DB2(sArtifactUnlockStore);
LOAD_DB2(sAuctionHouseStore);
+ LOAD_DB2(sAzeriteEmpoweredItemStore);
LOAD_DB2(sAzeriteEssenceStore);
LOAD_DB2(sAzeriteEssencePowerStore);
LOAD_DB2(sAzeriteItemStore);
@@ -539,6 +552,10 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sAzeriteKnowledgeMultiplierStore);
LOAD_DB2(sAzeriteLevelInfoStore);
LOAD_DB2(sAzeritePowerStore);
+ LOAD_DB2(sAzeritePowerSetMemberStore);
+ LOAD_DB2(sAzeriteTierUnlockStore);
+ LOAD_DB2(sAzeriteTierUnlockSetStore);
+ LOAD_DB2(sAzeriteUnlockMappingStore);
LOAD_DB2(sBankBagSlotPricesStore);
LOAD_DB2(sBannedAddonsStore);
LOAD_DB2(sBarberShopStyleStore);
@@ -776,6 +793,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
for (ArtifactPowerRankEntry const* artifactPowerRank : sArtifactPowerRankStore)
_artifactPowerRanks[std::pair<uint32, uint8>{ artifactPowerRank->ArtifactPowerID, artifactPowerRank->RankIndex }] = artifactPowerRank;
+ for (AzeriteEmpoweredItemEntry const* azeriteEmpoweredItem : sAzeriteEmpoweredItemStore)
+ _azeriteEmpoweredItems[azeriteEmpoweredItem->ItemID] = azeriteEmpoweredItem;
+
for (AzeriteEssencePowerEntry const* azeriteEssencePower : sAzeriteEssencePowerStore)
_azeriteEssencePowersByIdAndRank[std::pair<uint32, uint32>{ azeriteEssencePower->AzeriteEssenceID, azeriteEssencePower->Tier }] = azeriteEssencePower;
@@ -801,6 +821,17 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
}
}
+ for (AzeritePowerSetMemberEntry const* azeritePowerSetMember : sAzeritePowerSetMemberStore)
+ if (sAzeritePowerStore.LookupEntry(azeritePowerSetMember->AzeritePowerID))
+ _azeritePowers[azeritePowerSetMember->AzeritePowerSetID].push_back(azeritePowerSetMember);
+
+ 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);
+
ASSERT(BATTLE_PET_SPECIES_MAX_ID >= sBattlePetSpeciesStore.GetNumRows(),
"BATTLE_PET_SPECIES_MAX_ID (%d) must be equal to or greater than %u", BATTLE_PET_SPECIES_MAX_ID, sBattlePetSpeciesStore.GetNumRows());
@@ -973,6 +1004,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
for (ItemXBonusTreeEntry const* itemBonusTreeAssignment : sItemXBonusTreeStore)
_itemToBonusTree.insert({ itemBonusTreeAssignment->ItemID, itemBonusTreeAssignment->ItemBonusTreeID });
+ for (auto&& kvp : _azeriteEmpoweredItems)
+ LoadAzeriteEmpoweredItemUnlockMappings(azeriteUnlockMappings, kvp.first);
+
for (MapDifficultyEntry const* entry : sMapDifficultyStore)
_mapDifficulties[entry->MapID][entry->DifficultyID] = entry;
_mapDifficulties[0][0] = _mapDifficulties[1][0]; // map 0 is missing from MapDifficulty.dbc so we cheat a bit
@@ -1493,6 +1527,11 @@ ArtifactPowerRankEntry const* DB2Manager::GetArtifactPowerRank(uint32 artifactPo
return nullptr;
}
+AzeriteEmpoweredItemEntry const* DB2Manager::GetAzeriteEmpoweredItem(uint32 itemId) const
+{
+ return Trinity::Containers::MapGetValuePtr(_azeriteEmpoweredItems, itemId);
+}
+
bool DB2Manager::IsAzeriteItem(uint32 itemId) const
{
return std::find_if(sAzeriteItemStore.begin(), sAzeriteItemStore.end(),
@@ -1515,6 +1554,28 @@ AzeriteItemMilestonePowerEntry const* DB2Manager::GetAzeriteItemMilestonePower(u
return _azeriteItemMilestonePowerByEssenceSlot[slot];
}
+std::vector<AzeritePowerSetMemberEntry const*> const* DB2Manager::GetAzeritePowers(uint32 itemId) const
+{
+ if (AzeriteEmpoweredItemEntry const* azeriteEmpoweredItem = GetAzeriteEmpoweredItem(itemId))
+ return Trinity::Containers::MapGetValuePtr(_azeritePowers, azeriteEmpoweredItem->AzeritePowerSetID);
+
+ return nullptr;
+}
+
+uint32 DB2Manager::GetRequiredAzeriteLevelForAzeritePowerTier(uint32 azeriteUnlockSetId, ItemContext context, uint32 tier) const
+{
+ ASSERT(tier < MAX_AZERITE_EMPOWERED_TIER);
+ if (std::array<uint8, MAX_AZERITE_EMPOWERED_TIER> const* levels = Trinity::Containers::MapGetValuePtr(_azeriteTierUnlockLevels, std::make_pair(azeriteUnlockSetId, context)))
+ return (*levels)[tier];
+
+ AzeriteTierUnlockSetEntry const* azeriteTierUnlockSet = sAzeriteTierUnlockSetStore.LookupEntry(azeriteUnlockSetId);
+ if (azeriteTierUnlockSet && azeriteTierUnlockSet->Flags & AZERITE_TIER_UNLOCK_SET_FLAG_DEFAULT)
+ if (std::array<uint8, MAX_AZERITE_EMPOWERED_TIER> const* levels = Trinity::Containers::MapGetValuePtr(_azeriteTierUnlockLevels, std::make_pair(azeriteUnlockSetId, ItemContext::NONE)))
+ return (*levels)[tier];
+
+ return sAzeriteLevelInfoStore.GetNumRows();
+}
+
char const* DB2Manager::GetBroadcastTextValue(BroadcastTextEntry const* broadcastText, LocaleConstant locale /*= DEFAULT_LOCALE*/, uint8 gender /*= GENDER_MALE*/, bool forceGender /*= false*/)
{
if ((gender == GENDER_FEMALE || gender == GENDER_NONE) && (forceGender || broadcastText->Text1->Str[DEFAULT_LOCALE][0] != '\0'))
@@ -2017,12 +2078,62 @@ std::set<uint32> DB2Manager::GetItemBonusTree(uint32 itemId, ItemContext itemCon
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;
}
+void LoadAzeriteEmpoweredItemUnlockMappings(std::unordered_map<int32, std::vector<AzeriteUnlockMappingEntry const*>> const& azeriteUnlockMappingsBySet, uint32 itemId)
+{
+ ItemSparseEntry const* proto = sItemSparseStore.LookupEntry(itemId);
+ if (!proto)
+ return;
+
+ VisitItemBonusTree(itemId, [&azeriteUnlockMappingsBySet, proto](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(proto->ID, ItemContext(bonusTreeNode->ItemContext))] = selectedAzeriteUnlockMapping;
+ }
+ }
+ });
+}
+
ItemChildEquipmentEntry const* DB2Manager::GetItemChildEquipment(uint32 itemId) const
{
auto itr = _itemChildEquipment.find(itemId);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index b5a748885fb..1f484ad3957 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -274,10 +274,13 @@ public:
std::vector<ArtifactPowerEntry const*> GetArtifactPowers(uint8 artifactId) const;
std::unordered_set<uint32> const* GetArtifactPowerLinks(uint32 artifactPowerId) const;
ArtifactPowerRankEntry const* GetArtifactPowerRank(uint32 artifactPowerId, uint8 rank) const;
+ AzeriteEmpoweredItemEntry const* GetAzeriteEmpoweredItem(uint32 itemId) const;
bool IsAzeriteItem(uint32 itemId) const;
AzeriteEssencePowerEntry const* GetAzeriteEssencePower(uint32 azeriteEssenceId, uint32 rank) const;
std::vector<AzeriteItemMilestonePowerEntry const*> const& GetAzeriteItemMilestonePowers() const;
AzeriteItemMilestonePowerEntry const* GetAzeriteItemMilestonePower(uint8 slot) const;
+ std::vector<AzeritePowerSetMemberEntry const*> const* GetAzeritePowers(uint32 itemId) const;
+ uint32 GetRequiredAzeriteLevelForAzeritePowerTier(uint32 azeriteUnlockSetId, ItemContext context, uint32 tier) const;
static char const* GetBroadcastTextValue(BroadcastTextEntry const* broadcastText, LocaleConstant locale = DEFAULT_LOCALE, uint8 gender = GENDER_MALE, bool forceGender = false);
bool HasCharacterFacialHairStyle(uint8 race, uint8 gender, uint8 variationId) const;
bool HasCharSections(uint8 race, uint8 gender, CharBaseSectionVariation variation) const;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 76a4b6f9bbd..7cb2f8a4044 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -259,6 +259,14 @@ struct AuctionHouseEntry
uint8 ConsignmentRate;
};
+struct AzeriteEmpoweredItemEntry
+{
+ uint32 ID;
+ int32 ItemID;
+ uint32 AzeriteTierUnlockSetID;
+ uint32 AzeritePowerSetID;
+};
+
struct AzeriteEssenceEntry
{
LocalizedString* Name;
@@ -318,6 +326,41 @@ struct AzeritePowerEntry
int32 Flags;
};
+struct AzeritePowerSetMemberEntry
+{
+ uint32 ID;
+ int32 AzeritePowerSetID;
+ int32 AzeritePowerID;
+ int32 Class;
+ int32 Tier;
+ int32 OrderIndex;
+};
+
+struct AzeriteTierUnlockEntry
+{
+ uint32 ID;
+ uint8 ItemCreationContext;
+ uint8 Tier;
+ uint8 AzeriteLevel;
+ uint32 AzeriteTierUnlockSetID;
+};
+
+struct AzeriteTierUnlockSetEntry
+{
+ uint32 ID;
+ int32 Flags;
+};
+
+struct AzeriteUnlockMappingEntry
+{
+ uint32 ID;
+ int32 ItemLevel;
+ int32 ItemBonusListHead;
+ int32 ItemBonusListShoulders;
+ int32 ItemBonusListChest;
+ uint32 AzeriteUnlockMappingSetID;
+};
+
struct BankBagSlotPricesEntry
{
uint32 ID;
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index f0d756a768d..04c5be7834f 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -166,6 +166,8 @@ enum ArtifactPowerFlag : uint8
#define MAX_ARTIFACT_TIER 1
+#define MAX_AZERITE_EMPOWERED_TIER 5
+
#define MAX_AZERITE_ESSENCE_SLOT 3
#define MAX_AZERITE_ESSENCE_RANK 4
@@ -176,6 +178,11 @@ enum class AzeriteItemMilestoneType : int32
BonusStamina = 2
};
+enum AzeriteTierUnlockSetFlags
+{
+ AZERITE_TIER_UNLOCK_SET_FLAG_DEFAULT = 0x1
+};
+
#define BATTLE_PET_SPECIES_MAX_ID 2796
enum ChrSpecializationFlag
@@ -801,7 +808,8 @@ enum CharSectionType
enum Curves
{
- CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS = 1718
+ CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS = 1718,
+ CURVE_ID_AZERITE_EMPOWERED_ITEM_RESPEC_COST = 6785
};
enum Difficulty : uint8
@@ -991,6 +999,7 @@ enum ItemBonusType
ITEM_BONUS_BONDING = 16,
ITEM_BONUS_RELIC_TYPE = 17,
ITEM_BONUS_OVERRIDE_REQUIRED_LEVEL = 18,
+ ITEM_BONUS_AZERITE_TIER_UNLOCK_SET = 19,
ITEM_BONUS_OVERRIDE_CAN_DISENCHANT = 21,
ITEM_BONUS_OVERRIDE_CAN_SCRAP = 22
};
diff --git a/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.cpp b/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.cpp
new file mode 100644
index 00000000000..f0ae4dd172c
--- /dev/null
+++ b/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 "AzeriteEmpoweredItem.h"
+#include "DatabaseEnv.h"
+#include "DB2Stores.h"
+#include "Player.h"
+
+AzeriteEmpoweredItem::AzeriteEmpoweredItem()
+{
+ m_objectType |= TYPEMASK_AZERITE_EMPOWERED_ITEM;
+ m_objectTypeId = TYPEID_AZERITE_EMPOWERED_ITEM;
+
+ m_azeritePowers = nullptr;
+ m_maxTier = 0;
+}
+
+bool AzeriteEmpoweredItem::Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner)
+{
+ if (!Item::Create(guidlow, itemId, context, owner))
+ return false;
+
+ InitAzeritePowerData();
+ return true;
+}
+
+void AzeriteEmpoweredItem::SaveToDB(CharacterDatabaseTransaction& trans)
+{
+ Item::SaveToDB(trans);
+
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ for (uint32 i = 0; i < MAX_AZERITE_EMPOWERED_TIER; ++i)
+ stmt->setInt32(1 + i, m_azeriteEmpoweredItemData->Selections[i]);
+
+ trans->Append(stmt);
+}
+
+void AzeriteEmpoweredItem::LoadAzeriteEmpoweredItemData(Player const* owner, AzeriteEmpoweredItemData& azeriteEmpoweredItem)
+{
+ InitAzeritePowerData();
+ bool needSave = false;
+ if (m_azeritePowers)
+ {
+ for (int32 i = MAX_AZERITE_EMPOWERED_TIER; --i >= 0; )
+ {
+ int32 selection = azeriteEmpoweredItem.SelectedAzeritePowers[i];
+ if (GetTierForAzeritePower(Classes(owner->getClass()), selection) != i)
+ {
+ needSave = true;
+ break;
+ }
+
+ SetSelectedAzeritePower(i, selection);
+ }
+ }
+ else
+ needSave = true;
+
+ if (needSave)
+ {
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_AZERITE_EMPOWERED);
+ for (uint32 i = 0; i < MAX_AZERITE_EMPOWERED_TIER; ++i)
+ stmt->setInt32(i, m_azeriteEmpoweredItemData->Selections[i]);
+
+ stmt->setUInt64(5, GetGUID().GetCounter());
+ CharacterDatabase.Execute(stmt);
+ }
+}
+
+void AzeriteEmpoweredItem::DeleteFromDB(CharacterDatabaseTransaction& trans, ObjectGuid::LowType itemGuid)
+{
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, itemGuid);
+ CharacterDatabase.ExecuteOrAppend(trans, stmt);
+}
+
+void AzeriteEmpoweredItem::DeleteFromDB(CharacterDatabaseTransaction& trans)
+{
+ AzeriteEmpoweredItem::DeleteFromDB(trans, GetGUID().GetCounter());
+ Item::DeleteFromDB(trans);
+}
+
+uint32 AzeriteEmpoweredItem::GetRequiredAzeriteLevelForTier(uint32 tier) const
+{
+ return sDB2Manager.GetRequiredAzeriteLevelForAzeritePowerTier(_bonusData.AzeriteTierUnlockSetId, GetContext(), tier);
+}
+
+int32 AzeriteEmpoweredItem::GetTierForAzeritePower(Classes playerClass, int32 azeritePowerId) const
+{
+ auto azeritePowerItr = std::find_if(m_azeritePowers->begin(), m_azeritePowers->end(), [&](AzeritePowerSetMemberEntry const* power)
+ {
+ return power->AzeritePowerID == azeritePowerId && power->Class == playerClass;
+ });
+ if (azeritePowerItr != m_azeritePowers->end())
+ return (*azeritePowerItr)->Tier;
+
+ return MAX_AZERITE_EMPOWERED_TIER;
+}
+
+void AzeriteEmpoweredItem::SetSelectedAzeritePower(int32 tier, int32 azeritePowerId)
+{
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteEmpoweredItem::m_azeriteEmpoweredItemData).ModifyValue(&UF::AzeriteEmpoweredItemData::Selections, uint32(tier)), azeritePowerId);
+
+ // Not added to UF::ItemData::BonusListIDs, client fakes it on its own too
+ _bonusData.AddBonusList(sAzeritePowerStore.AssertEntry(azeritePowerId)->ItemBonusListID);
+}
+
+void AzeriteEmpoweredItem::ClearSelectedAzeritePowers()
+{
+ for (uint32 i = 0; i < MAX_AZERITE_EMPOWERED_TIER; ++i)
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteEmpoweredItem::m_azeriteEmpoweredItemData).ModifyValue(&UF::AzeriteEmpoweredItemData::Selections, i), 0);
+
+ _bonusData.Initialize(GetTemplate());
+ for (int32 bonusListID : *m_itemData->BonusListIDs)
+ _bonusData.AddBonusList(bonusListID);
+}
+
+int64 AzeriteEmpoweredItem::GetRespecCost() const
+{
+ if (Player const* owner = GetOwner())
+ return int64(GOLD * sDB2Manager.GetCurveValueAt(CURVE_ID_AZERITE_EMPOWERED_ITEM_RESPEC_COST, float(owner->GetNumRespecs())));
+
+ return MAX_MONEY_AMOUNT + 1;
+}
+
+void AzeriteEmpoweredItem::BuildValuesCreate(ByteBuffer* data, Player const* target) const
+{
+ UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target);
+ std::size_t sizePos = data->wpos();
+ *data << uint32(0);
+ *data << uint8(flags);
+ m_objectData->WriteCreate(*data, flags, this, target);
+ m_itemData->WriteCreate(*data, flags, this, target);
+ m_azeriteEmpoweredItemData->WriteCreate(*data, flags, this, target);
+ data->put<uint32>(sizePos, data->wpos() - sizePos - 4);
+}
+
+void AzeriteEmpoweredItem::BuildValuesUpdate(ByteBuffer* data, Player const* target) const
+{
+ UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target);
+ std::size_t sizePos = data->wpos();
+ *data << uint32(0);
+ *data << uint32(m_values.GetChangedObjectTypeMask());
+
+ if (m_values.HasChanged(TYPEID_OBJECT))
+ m_objectData->WriteUpdate(*data, flags, this, target);
+
+ if (m_values.HasChanged(TYPEID_ITEM))
+ m_itemData->WriteUpdate(*data, flags, this, target);
+
+ if (m_values.HasChanged(TYPEID_AZERITE_EMPOWERED_ITEM))
+ m_azeriteEmpoweredItemData->WriteUpdate(*data, flags, this, target);
+
+ data->put<uint32>(sizePos, data->wpos() - sizePos - 4);
+}
+
+void AzeriteEmpoweredItem::ClearUpdateMask(bool remove)
+{
+ m_values.ClearChangesMask(&AzeriteEmpoweredItem::m_azeriteEmpoweredItemData);
+ Item::ClearUpdateMask(remove);
+}
+
+void AzeriteEmpoweredItem::InitAzeritePowerData()
+{
+ m_azeritePowers = sDB2Manager.GetAzeritePowers(GetEntry());
+ if (m_azeritePowers)
+ {
+ m_maxTier = (*std::max_element(m_azeritePowers->begin(), m_azeritePowers->end(), [](AzeritePowerSetMemberEntry const* a1, AzeritePowerSetMemberEntry const* a2)
+ {
+ return a1->Tier < a2->Tier;
+ }))->Tier;
+ }
+}
diff --git a/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.h b/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.h
new file mode 100644
index 00000000000..eae1e79c565
--- /dev/null
+++ b/src/server/game/Entities/Item/AzeriteItem/AzeriteEmpoweredItem.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 AzeriteEmpoweredItem_h__
+#define AzeriteEmpoweredItem_h__
+
+#include "Item.h"
+
+class TC_GAME_API AzeriteEmpoweredItem : public Item
+{
+public:
+ AzeriteEmpoweredItem();
+
+ bool Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner) override;
+
+ void SaveToDB(CharacterDatabaseTransaction& trans) override;
+ void LoadAzeriteEmpoweredItemData(Player const* owner, AzeriteEmpoweredItemData& azeriteEmpoweredItem);
+ static void DeleteFromDB(CharacterDatabaseTransaction& trans, ObjectGuid::LowType itemGuid);
+ void DeleteFromDB(CharacterDatabaseTransaction& trans) override;
+
+ uint32 GetRequiredAzeriteLevelForTier(uint32 tier) const;
+ int32 GetTierForAzeritePower(Classes playerClass, int32 azeritePowerId) const;
+ int32 GetMaxAzeritePowerTier() const { return m_maxTier; }
+
+ uint32 GetSelectedAzeritePower(int32 tier) const { return uint32(m_azeriteEmpoweredItemData->Selections[uint32(tier)]); }
+ void SetSelectedAzeritePower(int32 tier, int32 azeritePowerId);
+ void ClearSelectedAzeritePowers();
+
+ int64 GetRespecCost() const;
+
+ void BuildValuesCreate(ByteBuffer* data, Player const* target) const override;
+ void BuildValuesUpdate(ByteBuffer* data, Player const* target) const override;
+ void ClearUpdateMask(bool remove) override;
+
+ UF::UpdateField<UF::AzeriteEmpoweredItemData, 0, TYPEID_AZERITE_EMPOWERED_ITEM> m_azeriteEmpoweredItemData;
+
+private:
+ void InitAzeritePowerData();
+ std::vector<AzeritePowerSetMemberEntry const*> const* m_azeritePowers;
+ int32 m_maxTier;
+};
+
+#endif // AzeriteEmpoweredItem_h__
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index e71c5fb1060..43daa90fba1 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -18,6 +18,7 @@
#include "Item.h"
#include "ArtifactPackets.h"
+#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "Bag.h"
#include "CollectionMgr.h"
@@ -51,6 +52,9 @@ Item* NewItemOrBag(ItemTemplate const* proto)
if (sDB2Manager.IsAzeriteItem(proto->GetId()))
return new AzeriteItem();
+ if (sDB2Manager.GetAzeriteEmpoweredItem(proto->GetId()))
+ return new AzeriteEmpoweredItem();
+
return new Item();
}
@@ -285,7 +289,8 @@ static uint32 const IllusionModifierMaskSpecSpecific =
void ItemAdditionalLoadInfo::Init(std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo>* loadInfo,
PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult,
- PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult)
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult,
+ PreparedQueryResult azeriteEmpoweredItemResult)
{
// 0 1 2 3 4 5
// SELECT a.itemGuid, a.xp, a.artifactAppearanceId, a.artifactTierId, ap.artifactPowerId, ap.purchasedRank FROM item_instance_artifact_powers ap LEFT JOIN item_instance_artifact a ON ap.itemGuid = a.itemGuid ...
@@ -396,6 +401,24 @@ void ItemAdditionalLoadInfo::Init(std::unordered_map<ObjectGuid::LowType, ItemAd
}
while (azeriteItemUnlockedEssencesResult->NextRow());
}
+
+ // 0 1 2 3 4 5
+ // SELECT iae.itemGuid, iae.azeritePowerId1, iae.azeritePowerId2, iae.azeritePowerId3, iae.azeritePowerId4, iae.azeritePowerId5 FROM item_instance_azerite_empowered iae INNER JOIN ...
+ if (azeriteEmpoweredItemResult)
+ {
+ do
+ {
+ Field* fields = azeriteEmpoweredItemResult->Fetch();
+ ItemAdditionalLoadInfo& info = (*loadInfo)[fields[0].GetUInt64()];
+ if (!info.AzeriteEmpoweredItem)
+ info.AzeriteEmpoweredItem = boost::in_place();
+
+ for (uint32 i = 0; i < MAX_AZERITE_EMPOWERED_TIER; ++i)
+ if (sAzeritePowerStore.LookupEntry(fields[1 + i].GetInt32()))
+ info.AzeriteEmpoweredItem->SelectedAzeritePowers[i] = fields[1 + i].GetInt32();
+
+ } while (azeriteEmpoweredItemResult->NextRow());
+ }
}
Item::Item()
@@ -1358,9 +1381,7 @@ void Item::SetGem(uint16 slot, ItemDynamicFieldGems const* gem, uint32 gemScalin
BonusData gemBonus;
gemBonus.Initialize(gemTemplate);
for (uint16 bonusListId : gem->BonusListIDs)
- if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId))
- for (ItemBonusEntry const* itemBonus : *bonuses)
- gemBonus.AddBonus(itemBonus->Type, itemBonus->Value);
+ gemBonus.AddBonusList(bonusListId);
uint32 gemBaseItemLevel = gemTemplate->GetBaseItemLevel();
if (ScalingStatDistributionEntry const* ssd = sScalingStatDistributionStore.LookupEntry(gemBonus.ScalingStatDistribution))
@@ -2508,9 +2529,7 @@ void Item::SetBonuses(std::vector<int32> bonusListIDs)
SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::BonusListIDs), std::move(bonusListIDs));
for (int32 bonusListID : *m_itemData->BonusListIDs)
- if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListID))
- for (ItemBonusEntry const* bonus : *bonuses)
- _bonusData.AddBonus(bonus->Type, bonus->Value);
+ _bonusData.AddBonusList(bonusListID);
SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemAppearanceModID), _bonusData.AppearanceModID);
}
@@ -2785,11 +2804,16 @@ void BonusData::Initialize(ItemTemplate const* proto)
RelicType = -1;
HasFixedLevel = false;
RequiredLevelOverride = 0;
+ AzeriteTierUnlockSetId = 0;
+ if (AzeriteEmpoweredItemEntry const* azeriteEmpoweredItem = sDB2Manager.GetAzeriteEmpoweredItem(proto->GetId()))
+ AzeriteTierUnlockSetId = azeriteEmpoweredItem->AzeriteTierUnlockSetID;
+
CanDisenchant = (proto->GetFlags() & ITEM_FLAG_NO_DISENCHANT) == 0;
CanScrap = (proto->GetFlags4() & ITEM_FLAG4_SCRAPABLE) != 0;
_state.AppearanceModPriority = std::numeric_limits<int32>::max();
_state.ScalingStatDistributionPriority = std::numeric_limits<int32>::max();
+ _state.AzeriteTierUnlockSetPriority = std::numeric_limits<int32>::max();
_state.HasQualityBonus = false;
}
@@ -2803,9 +2827,14 @@ void BonusData::Initialize(WorldPackets::Item::ItemInstance const& itemInstance)
if (itemInstance.ItemBonus)
for (uint32 bonusListID : itemInstance.ItemBonus->BonusListIDs)
- if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListID))
- for (ItemBonusEntry const* bonus : *bonuses)
- AddBonus(bonus->Type, bonus->Value);
+ AddBonusList(bonusListID);
+}
+
+void BonusData::AddBonusList(uint32 bonusListId)
+{
+ if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId))
+ for (ItemBonusEntry const* bonus : *bonuses)
+ AddBonus(bonus->Type, bonus->Value);
}
void BonusData::AddBonus(uint32 type, int32 const (&values)[3])
@@ -2883,6 +2912,13 @@ void BonusData::AddBonus(uint32 type, int32 const (&values)[3])
case ITEM_BONUS_OVERRIDE_REQUIRED_LEVEL:
RequiredLevelOverride = values[0];
break;
+ case ITEM_BONUS_AZERITE_TIER_UNLOCK_SET:
+ if (values[1] < _state.AzeriteTierUnlockSetPriority)
+ {
+ AzeriteTierUnlockSetId = values[0];
+ _state.AzeriteTierUnlockSetPriority = values[1];
+ }
+ break;
case ITEM_BONUS_OVERRIDE_CAN_DISENCHANT:
CanDisenchant = values[0] != 0;
break;
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index 3d55219ca83..6e8da9f1f12 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -92,12 +92,14 @@ struct BonusData
uint16 GemRelicRankBonus[MAX_ITEM_PROTO_SOCKETS];
int32 RelicType;
int32 RequiredLevelOverride;
+ int32 AzeriteTierUnlockSetId;
bool CanDisenchant;
bool CanScrap;
bool HasFixedLevel;
void Initialize(ItemTemplate const* proto);
void Initialize(WorldPackets::Item::ItemInstance const& itemInstance);
+ void AddBonusList(uint32 bonusListId);
void AddBonus(uint32 type, int32 const (&values)[3]);
private:
@@ -105,6 +107,7 @@ private:
{
int32 AppearanceModPriority;
int32 ScalingStatDistributionPriority;
+ int32 AzeriteTierUnlockSetPriority;
bool HasQualityBonus;
} _state;
};
@@ -140,13 +143,19 @@ struct AzeriteItemData
std::array<AzeriteItemSelectedEssencesData, MAX_SPECIALIZATIONS> SelectedAzeriteEssences = { };
};
+struct AzeriteEmpoweredItemData
+{
+ std::array<int32, MAX_AZERITE_EMPOWERED_TIER> SelectedAzeritePowers;
+};
+
struct ItemAdditionalLoadInfo
{
static void Init(std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo>* loadInfo, PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult,
- PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult);
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, PreparedQueryResult azeriteEmpoweredItemResult);
Optional<ArtifactData> Artifact;
Optional<AzeriteItemData> AzeriteItem;
+ Optional<AzeriteEmpoweredItemData> AzeriteEmpoweredItem;
};
struct ItemDynamicFieldGems
@@ -231,14 +240,17 @@ class TC_GAME_API Item : public Object
void SaveRefundDataToDB();
void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans);
- Bag* ToBag() { if (IsBag()) return reinterpret_cast<Bag*>(this); else return NULL; }
- const Bag* ToBag() const { if (IsBag()) return reinterpret_cast<const Bag*>(this); else return NULL; }
+ Bag* ToBag() { if (IsBag()) return reinterpret_cast<Bag*>(this); else return nullptr; }
+ Bag const* ToBag() const { if (IsBag()) return reinterpret_cast<Bag const*>(this); else return nullptr; }
AzeriteItem* ToAzeriteItem() { return IsAzeriteItem() ? reinterpret_cast<AzeriteItem*>(this) : nullptr; }
AzeriteItem const* ToAzeriteItem() const { return IsAzeriteItem() ? reinterpret_cast<AzeriteItem const*>(this) : nullptr; }
+ AzeriteEmpoweredItem* ToAzeriteEmpoweredItem() { return IsAzeriteEmpoweredItem() ? reinterpret_cast<AzeriteEmpoweredItem*>(this) : nullptr; }
+ AzeriteEmpoweredItem const* ToAzeriteEmpoweredItem() const { return IsAzeriteEmpoweredItem() ? reinterpret_cast<AzeriteEmpoweredItem const*>(this) : nullptr; }
bool IsLocked() const { return !HasItemFlag(ITEM_FIELD_FLAG_UNLOCKED); }
bool IsBag() const { return GetTemplate()->GetInventoryType() == INVTYPE_BAG; }
bool IsAzeriteItem() const { return GetTypeId() == TYPEID_AZERITE_ITEM; }
+ bool IsAzeriteEmpoweredItem() const { return GetTypeId() == TYPEID_AZERITE_EMPOWERED_ITEM; }
bool IsCurrencyToken() const { return GetTemplate()->IsCurrencyToken(); }
bool IsNotEmptyBag() const;
bool IsBroken() const { return *m_itemData->MaxDurability > 0 && *m_itemData->Durability == 0; }
@@ -411,6 +423,7 @@ class TC_GAME_API Item : public Object
UF::UpdateField<UF::ItemData, 0, TYPEID_ITEM> m_itemData;
protected:
+ void ApplyBonusList(uint32 itemBonusListId);
BonusData _bonusData;
private:
diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h
index 2c1717aea15..6141caec5fa 100644
--- a/src/server/game/Entities/Item/ItemTemplate.h
+++ b/src/server/game/Entities/Item/ItemTemplate.h
@@ -157,7 +157,7 @@ enum ItemFieldFlags : uint32
ITEM_FIELD_FLAG_CHILD = 0x00080000,
ITEM_FIELD_FLAG_UNK15 = 0x00100000,
ITEM_FIELD_FLAG_NEW_ITEM = 0x00200000, // Item glows in inventory
- ITEM_FIELD_FLAG_UNK17 = 0x00400000,
+ ITEM_FIELD_FLAG_AZERITE_EMPOWERED_ITEM_VIEWED = 0x00400000, // Won't play azerite powers animation when viewing it
ITEM_FIELD_FLAG_UNK18 = 0x00800000,
ITEM_FIELD_FLAG_UNK19 = 0x01000000,
ITEM_FIELD_FLAG_UNK20 = 0x02000000,
@@ -171,7 +171,7 @@ enum ItemFieldFlags : uint32
enum ItemFieldFlags2 : uint32
{
- ITEM_FIELD_FLAG2_HEART_OF_AZEROTH_EQUIPPED = 0x1
+ ITEM_FIELD_FLAG2_EQUIPPED = 0x1
};
enum ItemFlags : uint32
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e09b82e172f..eecffd43966 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -22,6 +22,7 @@
#include "AchievementMgr.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
+#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "Bag.h"
#include "Battlefield.h"
@@ -3853,8 +3854,13 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
PreparedQueryResult azeriteItemUnlockedEssencesResult = CharacterDatabase.Query(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, guid);
+ PreparedQueryResult azeriteEmpoweredItemResult = CharacterDatabase.Query(stmt);
+
std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo> additionalData;
- ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult, azeriteItemMilestonePowersResult, azeriteItemUnlockedEssencesResult);
+ ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult, azeriteItemMilestonePowersResult,
+ azeriteItemUnlockedEssencesResult, azeriteEmpoweredItemResult);
do
{
@@ -4078,6 +4084,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_EMPOWERED_BY_OWNER);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER);
stmt->setUInt64(0, guid);
trans->Append(stmt);
@@ -7934,6 +7944,13 @@ void Player::ApplyAzeritePowers(Item* item, bool apply)
ApplyAzeriteEssence(azeriteItem, selectedEssences->AzeriteEssenceID[slot], azeriteItem->GetEssenceRank(selectedEssences->AzeriteEssenceID[slot]),
AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(slot)->Type) == AzeriteItemMilestoneType::MajorEssence, apply);
}
+ else if (AzeriteEmpoweredItem* azeriteEmpoweredItem = item->ToAzeriteEmpoweredItem())
+ {
+ if (!apply || GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH, ITEM_SEARCH_IN_EQUIPMENT))
+ for (int32 i = 0; i < MAX_AZERITE_EMPOWERED_TIER; ++i)
+ if (AzeritePowerEntry const* azeritePower = sAzeritePowerStore.LookupEntry(azeriteEmpoweredItem->GetSelectedAzeritePower(i)))
+ ApplyAzeritePower(azeriteEmpoweredItem, azeritePower, apply);
+ }
}
void Player::ApplyAzeriteItemMilestonePower(AzeriteItem* item, AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower, bool apply)
@@ -8001,6 +8018,17 @@ void Player::ApplyAzeriteEssencePower(AzeriteItem* item, AzeriteEssencePowerEntr
}
}
+void Player::ApplyAzeritePower(AzeriteEmpoweredItem* item, AzeritePowerEntry const* azeritePower, bool apply)
+{
+ if (apply)
+ {
+ if (!azeritePower->SpecSetID || sDB2Manager.IsSpecSetMember(azeritePower->SpecSetID, GetPrimarySpecialization()))
+ CastSpell(this, azeritePower->SpellID, true, item);
+ }
+ else
+ RemoveAurasDueToItemSpell(azeritePower->SpellID, item->GetGUID());
+}
+
void Player::CastItemCombatSpell(DamageInfo const& damageInfo)
{
Unit* target = damageInfo.GetVictim();
@@ -8393,6 +8421,20 @@ void Player::ApplyAllAzeriteItemMods(bool apply)
}
}
+void Player::ApplyAllAzeriteEmpoweredItemMods(bool apply)
+{
+ for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ if (m_items[i])
+ {
+ if (!m_items[i]->IsAzeriteEmpoweredItem() || m_items[i]->IsBroken() || !CanUseAttackType(Player::GetAttackBySlot(i, m_items[i]->GetTemplate()->GetInventoryType())))
+ continue;
+
+ ApplyAzeritePowers(m_items[i], apply);
+ }
+ }
+}
+
ObjectGuid Player::GetLootWorldObjectGUID(ObjectGuid const& lootObjectGuid) const
{
auto itr = m_AELootView.find(lootObjectGuid);
@@ -12158,6 +12200,8 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
}
}
+ pItem->AddItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED);
+
if (IsInWorld() && update)
{
pItem->AddToWorld();
@@ -12331,6 +12375,8 @@ void Player::QuickEquipItem(uint16 pos, Item* pItem)
uint8 slot = pos & 255;
VisualizeItem(slot, pItem);
+ pItem->AddItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED);
+
if (IsInWorld())
{
pItem->AddToWorld();
@@ -12425,6 +12471,8 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update)
_ApplyItemMods(pItem, slot, false, update);
+ pItem->RemoveItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED);
+
// remove item dependent auras and casts (only weapon and armor slots)
if (slot < EQUIPMENT_SLOT_END)
{
@@ -17744,8 +17792,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder)
//"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
// 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
//"health, power1, power2, power3, power4, power5, power6, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, raidDifficulty, legacyRaidDifficulty, fishing_steps "
- // 72 73 74 75 76
- //"honor, honorLevel, prestigeLevel, honor_rest_state, honor_rest_bonus "
+ // 72 73 74 75 76
+ //"honor, honorLevel, honor_rest_state, honor_rest_bonus, numRespecs "
//
//"FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
@@ -18270,6 +18318,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder)
_LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS));
UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load
+ SetNumRespecs(fields[76].GetUInt8());
SetPrimarySpecialization(fields[36].GetUInt32());
SetActiveTalentGroup(fields[64].GetUInt8());
ChrSpecializationEntry const* primarySpec = sChrSpecializationStore.LookupEntry(GetPrimarySpecialization());
@@ -18324,6 +18373,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder)
holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AZERITE),
holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AZERITE_MILESTONE_POWERS),
holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AZERITE_UNLOCKED_ESSENCES),
+ holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AZERITE_EMPOWERED),
time_diff);
if (IsVoidStorageUnlocked())
@@ -18723,7 +18773,8 @@ void Player::LoadCorpse(PreparedQueryResult result)
}
void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult,
- PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, uint32 timeDiff)
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult,
+ PreparedQueryResult azeriteEmpoweredItemResult, uint32 timeDiff)
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12
// SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text,
@@ -18754,7 +18805,8 @@ void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult arti
//expected to be equipped before offhand items (@todo fixme)
std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo> additionalData;
- ItemAdditionalLoadInfo::Init(&additionalData, artifactsResult, azeriteResult, azeriteItemMilestonePowersResult, azeriteItemUnlockedEssencesResult);
+ ItemAdditionalLoadInfo::Init(&additionalData, artifactsResult, azeriteResult, azeriteItemMilestonePowersResult,
+ azeriteItemUnlockedEssencesResult, azeriteEmpoweredItemResult);
if (result)
{
@@ -18781,6 +18833,10 @@ void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult arti
if (addionalDataPtr->AzeriteItem)
if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
azeriteItem->LoadAzeriteItemData(this, *addionalDataPtr->AzeriteItem);
+
+ if (addionalDataPtr->AzeriteEmpoweredItem)
+ if (AzeriteEmpoweredItem* azeriteEmpoweredItem = item->ToAzeriteEmpoweredItem())
+ azeriteEmpoweredItem->LoadAzeriteEmpoweredItemData(this, *addionalDataPtr->AzeriteEmpoweredItem);
}
ObjectGuid bagGuid = fields[43].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[43].GetUInt64()) : ObjectGuid::Empty;
@@ -19087,6 +19143,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction& trans, uint32 zoneId, uint
Item::DeleteFromInventoryDB(trans, itemGuid);
Item::DeleteFromDB(trans, itemGuid);
AzeriteItem::DeleteFromDB(trans, itemGuid);
+ AzeriteEmpoweredItem::DeleteFromDB(trans, itemGuid);
}
return item;
}
@@ -19111,6 +19168,7 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
Item::DeleteFromDB(trans, itemGuid);
AzeriteItem::DeleteFromDB(trans, itemGuid);
+ AzeriteEmpoweredItem::DeleteFromDB(trans, itemGuid);
CharacterDatabase.CommitTransaction(trans);
return nullptr;
@@ -19143,6 +19201,10 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
if (addionalData->AzeriteItem)
if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
azeriteItem->LoadAzeriteItemData(player, *addionalData->AzeriteItem);
+
+ if (addionalData->AzeriteEmpoweredItem)
+ if (AzeriteEmpoweredItem* azeriteEmpoweredItem = item->ToAzeriteEmpoweredItem())
+ azeriteEmpoweredItem->LoadAzeriteEmpoweredItemData(player, *addionalData->AzeriteEmpoweredItem);
}
if (mail)
@@ -19234,8 +19296,13 @@ void Player::_LoadMail()
stmt->setUInt64(0, GetGUID().GetCounter());
PreparedQueryResult azeriteItemUnlockedEssencesResult = CharacterDatabase.Query(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ PreparedQueryResult azeriteEmpoweredItemResult = CharacterDatabase.Query(stmt);
+
std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo> additionalData;
- ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult, azeriteItemMilestonePowersResult, azeriteItemUnlockedEssencesResult);
+ ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult, azeriteItemMilestonePowersResult,
+ azeriteItemUnlockedEssencesResult, azeriteEmpoweredItemResult);
do
{
@@ -20328,6 +20395,7 @@ void Player::SaveToDB(bool create /*=false*/)
//save, but in tavern/city
stmt->setUInt32(index++, GetTalentResetCost());
stmt->setUInt32(index++, GetTalentResetTime());
+ stmt->setUInt8(index++, GetNumRespecs());
stmt->setUInt32(index++, GetPrimarySpecialization());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
@@ -20849,6 +20917,7 @@ void Player::_SaveMail(CharacterDatabaseTransaction& trans)
{
Item::DeleteFromDB(trans, itr2->item_guid);
AzeriteItem::DeleteFromDB(trans, itr2->item_guid);
+ AzeriteEmpoweredItem::DeleteFromDB(trans, itr2->item_guid);
}
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
@@ -27226,12 +27295,18 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec)
if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
{
if (azeriteItem->IsEquipped())
+ {
+ ApplyAllAzeriteEmpoweredItemMods(false);
ApplyAzeritePowers(azeriteItem, false);
+ }
azeriteItem->SetSelectedAzeriteEssences(spec->ID);
if (azeriteItem->IsEquipped())
+ {
ApplyAzeritePowers(azeriteItem, true);
+ ApplyAllAzeriteEmpoweredItemMods(true);
+ }
azeriteItem->SetState(ITEM_CHANGED, this);
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 8d82f256ff8..15e969ecaf2 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -41,6 +41,7 @@ struct AreaTriggerEntry;
struct ArtifactPowerRankEntry;
struct AzeriteEssencePowerEntry;
struct AzeriteItemMilestonePowerEntry;
+struct AzeritePowerEntry;
struct BarberShopStyleEntry;
struct CharTitlesEntry;
struct ChatChannelsEntry;
@@ -766,6 +767,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_AZERITE,
PLAYER_LOGIN_QUERY_LOAD_AZERITE_MILESTONE_POWERS,
PLAYER_LOGIN_QUERY_LOAD_AZERITE_UNLOCKED_ESSENCES,
+ PLAYER_LOGIN_QUERY_LOAD_AZERITE_EMPOWERED,
PLAYER_LOGIN_QUERY_LOAD_ACTIONS,
PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT,
PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE,
@@ -2015,6 +2017,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _ApplyAllItemMods();
void _ApplyAllLevelScaleItemMods(bool apply);
void ApplyAllAzeriteItemMods(bool apply);
+ void ApplyAllAzeriteEmpoweredItemMods(bool apply);
void _ApplyItemBonuses(Item* item, uint8 slot, bool apply);
void _ApplyWeaponDamage(uint8 slot, Item* item, bool apply);
bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) const;
@@ -2032,6 +2035,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ApplyAzeriteItemMilestonePower(AzeriteItem* item, AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower, bool apply);
void ApplyAzeriteEssence(AzeriteItem* item, uint32 azeriteEssenceId, uint32 rank, bool major, bool apply);
void ApplyAzeriteEssencePower(AzeriteItem* item, AzeriteEssencePowerEntry const* azeriteEssencePower, bool major, bool apply);
+ void ApplyAzeritePower(AzeriteEmpoweredItem* item, AzeritePowerEntry const* azeritePower, bool apply);
void CastItemCombatSpell(DamageInfo const& damageInfo);
void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto);
@@ -2450,6 +2454,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void RemovePlayerLocalFlag(PlayerLocalFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::LocalFlags), flags); }
void SetPlayerLocalFlags(PlayerLocalFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::LocalFlags), flags); }
+ uint8 GetNumRespecs() const { return m_activePlayerData->NumRespecs; }
+ void SetNumRespecs(uint8 numRespecs) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::NumRespecs), numRespecs); }
+
void SetWatchedFactionIndex(int32 index) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::WatchedFactionIndex), index); }
void AddAuraVision(PlayerFieldByte2Flags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::AuraVision), flags); }
@@ -2510,7 +2517,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadGlyphAuras();
void _LoadBoundInstances(PreparedQueryResult result);
void _LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult,
- PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, uint32 timeDiff);
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult,
+ PreparedQueryResult azeriteEmpoweredItemResult, uint32 timeDiff);
void _LoadVoidStorage(PreparedQueryResult result);
void _LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery);
void _LoadMail();
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 61c7b168a40..260514d55dd 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -18,6 +18,7 @@
#include "ObjectMgr.h"
#include "ArenaTeamMgr.h"
+#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "Chat.h"
#include "Containers.h"
@@ -5874,6 +5875,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
{
Item::DeleteFromDB(nonTransactional, itr2->item_guid);
AzeriteItem::DeleteFromDB(nonTransactional, itr2->item_guid);
+ AzeriteEmpoweredItem::DeleteFromDB(nonTransactional, itr2->item_guid);
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
diff --git a/src/server/game/Handlers/AzeriteHandler.cpp b/src/server/game/Handlers/AzeriteHandler.cpp
index 463511de039..fb3f3e01936 100644
--- a/src/server/game/Handlers/AzeriteHandler.cpp
+++ b/src/server/game/Handlers/AzeriteHandler.cpp
@@ -16,6 +16,7 @@
*/
#include "WorldSession.h"
+#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "AzeritePackets.h"
#include "DB2Stores.h"
@@ -168,3 +169,67 @@ void WorldSession::HandleAzeriteEssenceActivateEssence(WorldPackets::Azerite::Az
azeriteItem->SetState(ITEM_CHANGED, _player);
}
+
+void WorldSession::HandleAzeriteEmpoweredItemViewed(WorldPackets::Azerite::AzeriteEmpoweredItemViewed& azeriteEmpoweredItemViewed)
+{
+ Item* item = _player->GetItemByGuid(azeriteEmpoweredItemViewed.ItemGUID);
+ if (!item || !item->IsAzeriteEmpoweredItem())
+ return;
+
+ item->AddItemFlag(ITEM_FIELD_FLAG_AZERITE_EMPOWERED_ITEM_VIEWED);
+ item->SetState(ITEM_CHANGED, _player);
+}
+
+void WorldSession::HandleAzeriteEmpoweredItemSelectPower(WorldPackets::Azerite::AzeriteEmpoweredItemSelectPower& azeriteEmpoweredItemSelectPower)
+{
+ Item* item = _player->GetItemByPos(azeriteEmpoweredItemSelectPower.ContainerSlot, azeriteEmpoweredItemSelectPower.Slot);
+ if (!item)
+ return;
+
+ AzeritePowerEntry const* azeritePower = sAzeritePowerStore.LookupEntry(azeriteEmpoweredItemSelectPower.AzeritePowerID);
+ if (!azeritePower)
+ return;
+
+ AzeriteEmpoweredItem* azeriteEmpoweredItem = item->ToAzeriteEmpoweredItem();
+ if (!azeriteEmpoweredItem)
+ return;
+
+ // Validate tier
+ int32 actualTier = azeriteEmpoweredItem->GetTierForAzeritePower(Classes(_player->getClass()), azeriteEmpoweredItemSelectPower.AzeritePowerID);
+ if (azeriteEmpoweredItemSelectPower.Tier > MAX_AZERITE_EMPOWERED_TIER || azeriteEmpoweredItemSelectPower.Tier != actualTier)
+ return;
+
+ uint32 azeriteLevel = 0;
+ Item const* heartOfAzeroth = _player->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH, ITEM_SEARCH_EVERYWHERE);
+ if (!heartOfAzeroth)
+ return;
+
+ if (AzeriteItem const* azeriteItem = heartOfAzeroth->ToAzeriteItem())
+ azeriteLevel = azeriteItem->GetEffectiveLevel();
+
+ // Check required heart of azeroth level
+ if (azeriteLevel < azeriteEmpoweredItem->GetRequiredAzeriteLevelForTier(uint32(actualTier)))
+ return;
+
+ // tiers are ordered backwards, you first select the highest one
+ for (int32 i = actualTier + 1; i < azeriteEmpoweredItem->GetMaxAzeritePowerTier(); ++i)
+ if (!azeriteEmpoweredItem->GetSelectedAzeritePower(i))
+ return;
+
+ bool activateAzeritePower = azeriteEmpoweredItem->IsEquipped() && heartOfAzeroth->IsEquipped();
+ if (azeritePower->ItemBonusListID && activateAzeritePower)
+ _player->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), false);
+
+ azeriteEmpoweredItem->SetSelectedAzeritePower(actualTier, azeriteEmpoweredItemSelectPower.AzeritePowerID);
+
+ if (activateAzeritePower)
+ {
+ // apply all item mods when azerite power grants a bonus, item level changes and that affects stats and auras that scale with item level
+ if (azeritePower->ItemBonusListID)
+ _player->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), true);
+ else
+ _player->ApplyAzeritePower(azeriteEmpoweredItem, azeritePower, true);
+ }
+
+ azeriteEmpoweredItem->SetState(ITEM_CHANGED, _player);
+}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index b4d71da14bc..a7748e8a1bb 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -164,6 +164,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AZERITE_UNLOCKED_ESSENCES, stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_EMPOWERED);
+ stmt->setUInt64(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AZERITE_EMPOWERED, stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_VOID_STORAGE);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_VOID_STORAGE, stmt);
diff --git a/src/server/game/Server/Packets/AzeritePackets.cpp b/src/server/game/Server/Packets/AzeritePackets.cpp
index 9f300d2d72d..2321dab00a3 100644
--- a/src/server/game/Server/Packets/AzeritePackets.cpp
+++ b/src/server/game/Server/Packets/AzeritePackets.cpp
@@ -55,3 +55,31 @@ WorldPacket const* WorldPackets::Azerite::AzeriteEssenceSelectionResult::Write()
return &_worldPacket;
}
+
+void WorldPackets::Azerite::AzeriteEmpoweredItemViewed::Read()
+{
+ _worldPacket >> ItemGUID;
+}
+
+void WorldPackets::Azerite::AzeriteEmpoweredItemSelectPower::Read()
+{
+ _worldPacket >> Tier;
+ _worldPacket >> AzeritePowerID;
+ _worldPacket >> ContainerSlot;
+ _worldPacket >> Slot;
+}
+
+WorldPacket const* WorldPackets::Azerite::AzeriteEmpoweredItemEquippedStatusChanged::Write()
+{
+ _worldPacket.WriteBit(IsHeartEquipped);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::Azerite::AzeriteEmpoweredItemRespecOpen::Write()
+{
+ _worldPacket << NpcGUID;
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/AzeritePackets.h b/src/server/game/Server/Packets/AzeritePackets.h
index fea3a799db6..1c9ed0ee9bf 100644
--- a/src/server/game/Server/Packets/AzeritePackets.h
+++ b/src/server/game/Server/Packets/AzeritePackets.h
@@ -89,6 +89,49 @@ namespace WorldPackets
int32 AzeriteEssenceID = 0;
Optional<uint8> Slot;
};
+
+ class AzeriteEmpoweredItemViewed final : public ClientPacket
+ {
+ public:
+ AzeriteEmpoweredItemViewed(WorldPacket&& packet) : ClientPacket(CMSG_AZERITE_EMPOWERED_ITEM_VIEWED, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid ItemGUID;
+ };
+
+ class AzeriteEmpoweredItemSelectPower final : public ClientPacket
+ {
+ public:
+ AzeriteEmpoweredItemSelectPower(WorldPacket&& packet) : ClientPacket(CMSG_AZERITE_EMPOWERED_ITEM_SELECT_POWER, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 Tier = 0;
+ int32 AzeritePowerID = 0;
+ uint8 ContainerSlot = 0;
+ uint8 Slot = 0;
+ };
+
+ class AzeriteEmpoweredItemEquippedStatusChanged final : public ServerPacket
+ {
+ public:
+ AzeriteEmpoweredItemEquippedStatusChanged() : ServerPacket(SMSG_AZERITE_EMPOWERED_ITEM_EQUIPPED_STATUS_CHANGED, 1) { }
+
+ WorldPacket const* Write() override;
+
+ bool IsHeartEquipped = false;
+ };
+
+ class AzeriteEmpoweredItemRespecOpen final : public ServerPacket
+ {
+ public:
+ AzeriteEmpoweredItemRespecOpen(ObjectGuid npcGuid) : ServerPacket(SMSG_AZERITE_EMPOWERED_ITEM_RESPEC_OPEN, 1), NpcGUID(npcGuid) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid NpcGUID;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index d7a6c6f7f94..f42b3d93a45 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -182,8 +182,8 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_AUTO_EQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemOpcode);
DEFINE_HANDLER(CMSG_AUTO_EQUIP_ITEM_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemSlotOpcode);
DEFINE_HANDLER(CMSG_AUTO_STORE_BAG_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBagItemOpcode);
- DEFINE_HANDLER(CMSG_AZERITE_EMPOWERED_ITEM_SELECT_POWER, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_AZERITE_EMPOWERED_ITEM_VIEWED, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_AZERITE_EMPOWERED_ITEM_SELECT_POWER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAzeriteEmpoweredItemSelectPower);
+ DEFINE_HANDLER(CMSG_AZERITE_EMPOWERED_ITEM_VIEWED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAzeriteEmpoweredItemViewed);
DEFINE_HANDLER(CMSG_AZERITE_ESSENCE_ACTIVATE_ESSENCE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAzeriteEssenceActivateEssence);
DEFINE_HANDLER(CMSG_AZERITE_ESSENCE_UNLOCK_MILESTONE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAzeriteEssenceUnlockMilestone);
DEFINE_HANDLER(CMSG_BANKER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBankerActivateOpcode);
@@ -947,8 +947,8 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AVAILABLE_HOTFIXES, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_EMPOWERED_ITEM_EQUIPPED_STATUS_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_EMPOWERED_ITEM_RESPEC_OPEN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_EMPOWERED_ITEM_EQUIPPED_STATUS_CHANGED, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_EMPOWERED_ITEM_RESPEC_OPEN, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_ESSENCE_FORGE_CLOSE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_ESSENCE_FORGE_OPENED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_ESSENCE_SELECTION_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 808359495b6..7f286edcd2b 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -111,6 +111,8 @@ namespace WorldPackets
namespace Azerite
{
+ class AzeriteEmpoweredItemSelectPower;
+ class AzeriteEmpoweredItemViewed;
class AzeriteEssenceUnlockMilestone;
class AzeriteEssenceActivateEssence;
}
@@ -1727,6 +1729,8 @@ class TC_GAME_API WorldSession
// Azerite
void HandleAzeriteEssenceUnlockMilestone(WorldPackets::Azerite::AzeriteEssenceUnlockMilestone& azeriteEssenceUnlockMilestone);
void HandleAzeriteEssenceActivateEssence(WorldPackets::Azerite::AzeriteEssenceActivateEssence& azeriteEssenceActivateEssence);
+ void HandleAzeriteEmpoweredItemViewed(WorldPackets::Azerite::AzeriteEmpoweredItemViewed& azeriteEmpoweredItemViewed);
+ void HandleAzeriteEmpoweredItemSelectPower(WorldPackets::Azerite::AzeriteEmpoweredItemSelectPower& azeriteEmpoweredItemSelectPower);
union ConnectToKey
{
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b29b6b7f2c2..7d4eadb32d4 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -17,6 +17,7 @@
*/
#include "Spell.h"
+#include "AzeriteEmpoweredItem.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "Battleground.h"
@@ -6717,6 +6718,37 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
}
break;
}
+ case SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM:
+ {
+ Item const* item = m_targets.GetItemTarget();
+ if (!item)
+ return SPELL_FAILED_AZERITE_EMPOWERED_ONLY;
+
+ if (item->GetOwnerGUID() != m_caster->GetGUID())
+ return SPELL_FAILED_DONT_REPORT;
+
+ AzeriteEmpoweredItem const* azeriteEmpoweredItem = item->ToAzeriteEmpoweredItem();
+ if (!azeriteEmpoweredItem)
+ return SPELL_FAILED_AZERITE_EMPOWERED_ONLY;
+
+ bool hasSelections = false;
+ for (int32 tier = 0; tier < azeriteEmpoweredItem->GetMaxAzeritePowerTier(); ++tier)
+ {
+ if (azeriteEmpoweredItem->GetSelectedAzeritePower(tier))
+ {
+ hasSelections = true;
+ break;
+ }
+ }
+
+ if (!hasSelections)
+ return SPELL_FAILED_AZERITE_EMPOWERED_NO_CHOICES_TO_UNDO;
+
+ if (!m_caster->ToPlayer()->HasEnoughMoney(azeriteEmpoweredItem->GetRespecCost()))
+ return SPELL_FAILED_DONT_REPORT;
+
+ break;
+ }
default:
break;
}
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 2ca0994b736..61ed55efa94 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -484,6 +484,7 @@ class TC_GAME_API Spell
void EffectPlayScene(SpellEffIndex effIndex);
void EffectGiveHonor(SpellEffIndex effIndex);
void EffectLearnTransmogSet(SpellEffIndex effIndex);
+ void EffectRespecAzeriteEmpoweredItem(SpellEffIndex effIndex);
void EffectLearnAzeriteEssencePower(SpellEffIndex effIndex);
typedef std::unordered_set<Aura*> UsedSpellMods;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 4b6b0f504d1..dd3c48af04f 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -19,6 +19,7 @@
#include "Spell.h"
#include "AccountMgr.h"
#include "AreaTrigger.h"
+#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "Battleground.h"
#include "BattlegroundMgr.h"
@@ -337,7 +338,7 @@ NonDefaultConstructible<pEffect> SpellEffects[TOTAL_SPELL_EFFECTS] =
&Spell::EffectNULL, //256 SPELL_EFFECT_256
&Spell::EffectNULL, //257 SPELL_EFFECT_257
&Spell::EffectNULL, //258 SPELL_EFFECT_MODIFY_KEYSTONE
- &Spell::EffectNULL, //259 SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM
+ &Spell::EffectRespecAzeriteEmpoweredItem, //259 SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM
&Spell::EffectNULL, //260 SPELL_EFFECT_SUMMON_STABLED_PET
&Spell::EffectNULL, //261 SPELL_EFFECT_SCRAP_ITEM
&Spell::EffectNULL, //262 SPELL_EFFECT_262
@@ -5821,6 +5822,34 @@ void Spell::EffectLearnTransmogSet(SpellEffIndex /*effIndex*/)
unitTarget->ToPlayer()->GetSession()->GetCollectionMgr()->AddTransmogSet(effectInfo->MiscValue);
}
+void Spell::EffectRespecAzeriteEmpoweredItem(SpellEffIndex /*effIndex*/)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ if (!itemTarget || !itemTarget->IsAzeriteEmpoweredItem())
+ return;
+
+ Player* owner = m_caster->ToPlayer();
+ if (!owner)
+ return;
+
+ AzeriteEmpoweredItem* azeriteEmpoweredItem = itemTarget->ToAzeriteEmpoweredItem();
+ owner->ModifyMoney(-azeriteEmpoweredItem->GetRespecCost());
+
+ // reapply all item mods - item level change affects stats and auras
+ if (azeriteEmpoweredItem->IsEquipped())
+ owner->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), false);
+
+ azeriteEmpoweredItem->ClearSelectedAzeritePowers();
+
+ if (azeriteEmpoweredItem->IsEquipped())
+ owner->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), true);
+
+ azeriteEmpoweredItem->SetState(ITEM_CHANGED, owner);
+ owner->SetNumRespecs(owner->GetNumRespecs() + 1);
+}
+
void Spell::EffectLearnAzeriteEssencePower(SpellEffIndex /*effIndex*/)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index d1c611dcd6e..b5b7af67161 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -22,6 +22,7 @@
*/
#include "ScriptMgr.h"
+#include "AzeritePackets.h"
#include "Battleground.h"
#include "CreatureAIImpl.h"
#include "DB2Stores.h"
@@ -4776,16 +4777,24 @@ class spell_item_heart_of_azeroth : public AuraScript
void SetEquippedFlag(AuraEffect const* /*effect*/, AuraEffectHandleModes /*mode*/)
{
- if (Player* target = GetTarget()->ToPlayer())
- if (Item* item = target->GetItemByGuid(GetAura()->GetCastItemGUID()))
- item->AddItemFlag2(ITEM_FIELD_FLAG2_HEART_OF_AZEROTH_EQUIPPED);
+ SetState(true);
}
void ClearEquippedFlag(AuraEffect const* /*effect*/, AuraEffectHandleModes /*mode*/)
{
+ SetState(false);
+ }
+
+ void SetState(bool equipped)
+ {
if (Player* target = GetTarget()->ToPlayer())
- if (Item* item = target->GetItemByGuid(GetAura()->GetCastItemGUID()))
- item->RemoveItemFlag2(ITEM_FIELD_FLAG2_HEART_OF_AZEROTH_EQUIPPED);
+ {
+ target->ApplyAllAzeriteEmpoweredItemMods(equipped);
+
+ WorldPackets::Azerite::AzeriteEmpoweredItemEquippedStatusChanged statusChanged;
+ statusChanged.IsHeartEquipped = equipped;
+ target->SendDirectMessage(statusChanged.Write());
+ }
}
void Register()