diff options
author | Shauren <shauren.trinity@gmail.com> | 2019-12-05 00:12:35 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2019-12-05 00:12:35 +0100 |
commit | d934824421c83598853487c5cc9e4cbb3c5d0006 (patch) | |
tree | a63415c891a43c417747edeae37ac15ecb4a2389 | |
parent | 65b91375e0a3b01eaacab77f973429b544c63a61 (diff) |
Core/Items: Implement azerite empowered items
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() |