aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp28
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h10
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp20
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h12
-rw-r--r--src/server/game/Achievements/CriteriaHandler.cpp7
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h86
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp63
-rw-r--r--src/server/game/DataStores/DB2Stores.h7
-rw-r--r--src/server/game/DataStores/DB2Structure.h46
-rw-r--r--src/server/game/DataStores/DBCEnums.h10
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp43
-rw-r--r--src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp211
-rw-r--r--src/server/game/Entities/Item/AzeriteItem/AzeriteItem.h28
-rw-r--r--src/server/game/Entities/Item/Item.cpp79
-rw-r--r--src/server/game/Entities/Item/Item.h28
-rw-r--r--src/server/game/Entities/Item/ItemDefines.h16
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateField.h5
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.cpp4
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.h5
-rw-r--r--src/server/game/Entities/Player/Player.cpp138
-rw-r--r--src/server/game/Entities/Player/Player.h15
-rw-r--r--src/server/game/Groups/Group.cpp2
-rw-r--r--src/server/game/Handlers/AzeriteHandler.cpp164
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp8
-rw-r--r--src/server/game/Handlers/InspectHandler.cpp5
-rw-r--r--src/server/game/Server/Packets/AzeritePackets.cpp31
-rw-r--r--src/server/game/Server/Packets/AzeritePackets.h54
-rw-r--r--src/server/game/Server/Packets/InspectPackets.cpp23
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp10
-rw-r--r--src/server/game/Server/WorldSession.h10
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp2
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp42
33 files changed, 1152 insertions, 61 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index ba56adc790a..ec583f3ba20 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -146,7 +146,14 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT " SelectItemInstanceContent ", ii.owner_guid, m.id FROM mail_items mi INNER JOIN mail m ON mi.mail_id = m.id LEFT JOIN item_instance ii ON mi.item_guid = 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 WHERE m.receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAILITEMS_ARTIFACT, "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 INNER JOIN mail_items mi ON a.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_MAILITEMS_AZERITE, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel 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, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel, "
+ "iz.selectedAzeriteEssences1specId, iz.selectedAzeriteEssences1azeriteEssenceId1, iz.selectedAzeriteEssences1azeriteEssenceId2, iz.selectedAzeriteEssences1azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences2specId, iz.selectedAzeriteEssences2azeriteEssenceId1, iz.selectedAzeriteEssences2azeriteEssenceId2, iz.selectedAzeriteEssences2azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences3specId, iz.selectedAzeriteEssences3azeriteEssenceId1, iz.selectedAzeriteEssences3azeriteEssenceId2, iz.selectedAzeriteEssences3azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences4specId, iz.selectedAzeriteEssences4azeriteEssenceId1, iz.selectedAzeriteEssences4azeriteEssenceId2, iz.selectedAzeriteEssences4azeriteEssenceId3 "
+ "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_ITEM_INSTANCE_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_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEM_INSTANCE_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_ASYNC);
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);
@@ -191,11 +198,26 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_ITEM_INSTANCE_MODIFIERS, "INSERT INTO item_instance_modifiers (itemGuid, fixedScalingLevel, artifactKnowledgeLevel) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS, "DELETE FROM item_instance_modifiers WHERE itemGuid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS_BY_OWNER, "DELETE im FROM item_instance_modifiers im LEFT JOIN item_instance ii ON im.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel FROM item_instance_azerite iz INNER JOIN character_inventory ci ON iz.itemGuid = ci.item WHERE ci.guid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_INS_ITEM_INSTANCE_AZERITE, "INSERT INTO item_instance_azerite (itemGuid, xp, level, knowledgeLevel) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel, "
+ "iz.selectedAzeriteEssences1specId, iz.selectedAzeriteEssences1azeriteEssenceId1, iz.selectedAzeriteEssences1azeriteEssenceId2, iz.selectedAzeriteEssences1azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences2specId, iz.selectedAzeriteEssences2azeriteEssenceId1, iz.selectedAzeriteEssences2azeriteEssenceId2, iz.selectedAzeriteEssences2azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences3specId, iz.selectedAzeriteEssences3azeriteEssenceId1, iz.selectedAzeriteEssences3azeriteEssenceId2, iz.selectedAzeriteEssences3azeriteEssenceId3, "
+ "iz.selectedAzeriteEssences4specId, iz.selectedAzeriteEssences4azeriteEssenceId1, iz.selectedAzeriteEssences4azeriteEssenceId2, iz.selectedAzeriteEssences4azeriteEssenceId3 "
+ "FROM item_instance_azerite iz INNER JOIN character_inventory ci ON iz.itemGuid = ci.item WHERE ci.guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEM_INSTANCE_AZERITE, "INSERT INTO item_instance_azerite (itemGuid, xp, level, knowledgeLevel, selectedAzeriteEssences1specId, selectedAzeriteEssences1azeriteEssenceId1, selectedAzeriteEssences1azeriteEssenceId2, selectedAzeriteEssences1azeriteEssenceId3, "
+ "selectedAzeriteEssences2specId, selectedAzeriteEssences2azeriteEssenceId1, selectedAzeriteEssences2azeriteEssenceId2, selectedAzeriteEssences2azeriteEssenceId3, selectedAzeriteEssences3specId, selectedAzeriteEssences3azeriteEssenceId1, selectedAzeriteEssences3azeriteEssenceId2, selectedAzeriteEssences3azeriteEssenceId3, "
+ "selectedAzeriteEssences4specId, selectedAzeriteEssences4azeriteEssenceId1, selectedAzeriteEssences4azeriteEssenceId2, selectedAzeriteEssences4azeriteEssenceId3) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ITEM_INSTANCE_AZERITE_ON_LOAD, "UPDATE item_instance_azerite SET xp = ?, knowledgeLevel = ? WHERE itemGuid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE, "DELETE FROM item_instance_azerite WHERE itemGuid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_BY_OWNER, "DELETE iz FROM item_instance_azerite iz LEFT JOIN item_instance ii ON iz.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER, "SELECT iamp.itemGuid, iamp.azeriteItemMilestonePowerId FROM item_instance_azerite_milestone_power iamp INNER JOIN character_inventory ci ON iamp.itemGuid = ci.item WHERE ci.guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_MILESTONE_POWER, "INSERT INTO item_instance_azerite_milestone_power (itemGuid, azeriteItemMilestonePowerId) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER, "DELETE FROM item_instance_azerite_milestone_power WHERE itemGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER_BY_OWNER, "DELETE iamp FROM item_instance_azerite_milestone_power iamp LEFT JOIN item_instance ii ON iamp.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE, "SELECT iaue.itemGuid, iaue.azeriteEssenceId, iaue.`rank` FROM item_instance_azerite_unlocked_essence iaue INNER JOIN character_inventory ci ON iaue.itemGuid = ci.item WHERE ci.guid = ?", CONNECTION_ASYNC);
+ 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_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);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 63e2aac6833..cd3750702a6 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -118,6 +118,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_MAILITEMS,
CHAR_SEL_MAILITEMS_ARTIFACT,
CHAR_SEL_MAILITEMS_AZERITE,
+ CHAR_SEL_MAILITEMS_AZERITE_MILESTONE_POWER,
+ CHAR_SEL_MAILITEMS_AZERITE_UNLOCKED_ESSENCE,
CHAR_SEL_AUCTION_ITEMS,
CHAR_INS_AUCTION,
CHAR_DEL_AUCTION,
@@ -165,6 +167,14 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_ITEM_INSTANCE_AZERITE_ON_LOAD,
CHAR_DEL_ITEM_INSTANCE_AZERITE,
CHAR_DEL_ITEM_INSTANCE_AZERITE_BY_OWNER,
+ CHAR_SEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER,
+ CHAR_INS_ITEM_INSTANCE_AZERITE_MILESTONE_POWER,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER_BY_OWNER,
+ CHAR_SEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE,
+ CHAR_INS_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE,
+ CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE_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 daa8fade9c4..fd3c2d06888 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -110,9 +110,23 @@ 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);
+ // 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);
+
+ // AzeriteEssencePower.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_ESSENCE_POWER, "SELECT ID, SourceAlliance, SourceHorde, AzeriteEssenceID, Tier, MajorPowerDescription, "
+ "MinorPowerDescription, MajorPowerActual, MinorPowerActual FROM azerite_essence_power ORDER BY ID DESC", CONNECTION_SYNCH);
+ PREPARE_LOCALE_STMT(HOTFIX_SEL_AZERITE_ESSENCE_POWER, "SELECT ID, SourceAlliance_lang, SourceHorde_lang FROM azerite_essence_power_locale"
+ " WHERE locale = ?", CONNECTION_SYNCH);
+
// AzeriteItem.db2
PrepareStatement(HOTFIX_SEL_AZERITE_ITEM, "SELECT ID, ItemID FROM azerite_item ORDER BY ID DESC", CONNECTION_SYNCH);
+ // AzeriteItemMilestonePower.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_ITEM_MILESTONE_POWER, "SELECT ID, RequiredLevel, AzeritePowerID, Type, AutoUnlock"
+ " FROM azerite_item_milestone_power ORDER BY ID DESC", CONNECTION_SYNCH);
+
// AzeriteKnowledgeMultiplier.db2
PrepareStatement(HOTFIX_SEL_AZERITE_KNOWLEDGE_MULTIPLIER, "SELECT ID, Multiplier FROM azerite_knowledge_multiplier ORDER BY ID DESC", CONNECTION_SYNCH);
@@ -120,6 +134,9 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PrepareStatement(HOTFIX_SEL_AZERITE_LEVEL_INFO, "SELECT ID, BaseExperienceToNextLevel, MinimumExperienceToNextLevel, ItemLevel"
" FROM azerite_level_info ORDER BY ID DESC", CONNECTION_SYNCH);
+ // AzeritePower.db2
+ PrepareStatement(HOTFIX_SEL_AZERITE_POWER, "SELECT ID, SpellID, ItemBonusListID, SpecSetID, Flags FROM azerite_power 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);
@@ -841,6 +858,9 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" FROM specialization_spells ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_SPECIALIZATION_SPELLS, "SELECT ID, Description_lang FROM specialization_spells_locale WHERE locale = ?", CONNECTION_SYNCH);
+ // SpecSetMember.db2
+ PrepareStatement(HOTFIX_SEL_SPEC_SET_MEMBER, "SELECT ID, ChrSpecializationID, SpecSetID FROM spec_set_member ORDER BY ID DESC", CONNECTION_SYNCH);
+
// SpellAuraOptions.db2
PrepareStatement(HOTFIX_SEL_SPELL_AURA_OPTIONS, "SELECT ID, DifficultyID, CumulativeAura, ProcCategoryRecovery, ProcChance, ProcCharges, "
"SpellProcsPerMinuteID, ProcTypeMask1, ProcTypeMask2, SpellID FROM spell_aura_options 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 0038716a43d..ab5eea98c41 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -75,12 +75,22 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_AUCTION_HOUSE,
HOTFIX_SEL_AUCTION_HOUSE_LOCALE,
+ HOTFIX_SEL_AZERITE_ESSENCE,
+ HOTFIX_SEL_AZERITE_ESSENCE_LOCALE,
+
+ HOTFIX_SEL_AZERITE_ESSENCE_POWER,
+ HOTFIX_SEL_AZERITE_ESSENCE_POWER_LOCALE,
+
HOTFIX_SEL_AZERITE_ITEM,
+ HOTFIX_SEL_AZERITE_ITEM_MILESTONE_POWER,
+
HOTFIX_SEL_AZERITE_KNOWLEDGE_MULTIPLIER,
HOTFIX_SEL_AZERITE_LEVEL_INFO,
+ HOTFIX_SEL_AZERITE_POWER,
+
HOTFIX_SEL_BANK_BAG_SLOT_PRICES,
HOTFIX_SEL_BANNED_ADDONS,
@@ -444,6 +454,8 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_SPECIALIZATION_SPELLS,
HOTFIX_SEL_SPECIALIZATION_SPELLS_LOCALE,
+ HOTFIX_SEL_SPEC_SET_MEMBER,
+
HOTFIX_SEL_SPELL_AURA_OPTIONS,
HOTFIX_SEL_SPELL_AURA_RESTRICTIONS,
diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp
index 742a864d026..b0154ee5209 100644
--- a/src/server/game/Achievements/CriteriaHandler.cpp
+++ b/src/server/game/Achievements/CriteriaHandler.cpp
@@ -2044,8 +2044,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
return false;
break;
case CRITERIA_ADDITIONAL_CONDITION_REWARDED_QUEST: // 110
- if (!referencePlayer->GetQuestRewardStatus(reqValue))
- return false;
+ if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(reqValue))
+ if (!(referencePlayer->m_activePlayerData->QuestCompleted[((questBit - 1) >> 6)] & (UI64LIT(1) << ((questBit - 1) & 63))))
+ return false;
break;
case CRITERIA_ADDITIONAL_CONDITION_COMPLETED_QUEST: // 111
if (referencePlayer->GetQuestStatus(reqValue) != QUEST_STATUS_COMPLETE)
@@ -2610,7 +2611,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
case CRITERIA_ADDITIONAL_CONDITION_AZERITE_ITEM_LEVEL: // 235
{
Item const* heartOfAzeroth = referencePlayer->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH);
- if (!heartOfAzeroth || heartOfAzeroth->ToAzeriteItem()->m_azeriteItemData->Level < reqValue)
+ if (!heartOfAzeroth || heartOfAzeroth->ToAzeriteItem()->GetLevel() < reqValue)
return false;
break;
}
diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h
index bce776c6034..a02d39bd44f 100644
--- a/src/server/game/DataStores/DB2LoadInfo.h
+++ b/src/server/game/DataStores/DB2LoadInfo.h
@@ -418,6 +418,43 @@ struct AuctionHouseLoadInfo
}
};
+struct AzeriteEssenceLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_STRING, "Name" },
+ { false, FT_STRING, "Description" },
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "SpecSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteEssenceMeta::Instance(), HOTFIX_SEL_AZERITE_ESSENCE);
+ return &loadInfo;
+ }
+};
+
+struct AzeriteEssencePowerLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_STRING, "SourceAlliance" },
+ { false, FT_STRING, "SourceHorde" },
+ { true, FT_INT, "AzeriteEssenceID" },
+ { false, FT_BYTE, "Tier" },
+ { true, FT_INT, "MajorPowerDescription" },
+ { true, FT_INT, "MinorPowerDescription" },
+ { true, FT_INT, "MajorPowerActual" },
+ { true, FT_INT, "MinorPowerActual" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteEssencePowerMeta::Instance(), HOTFIX_SEL_AZERITE_ESSENCE_POWER);
+ return &loadInfo;
+ }
+};
+
struct AzeriteItemLoadInfo
{
static DB2LoadInfo const* Instance()
@@ -432,6 +469,23 @@ struct AzeriteItemLoadInfo
}
};
+struct AzeriteItemMilestonePowerLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "RequiredLevel" },
+ { true, FT_INT, "AzeritePowerID" },
+ { true, FT_INT, "Type" },
+ { true, FT_INT, "AutoUnlock" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeriteItemMilestonePowerMeta::Instance(), HOTFIX_SEL_AZERITE_ITEM_MILESTONE_POWER);
+ return &loadInfo;
+ }
+};
+
struct AzeriteKnowledgeMultiplierLoadInfo
{
static DB2LoadInfo const* Instance()
@@ -462,6 +516,23 @@ struct AzeriteLevelInfoLoadInfo
}
};
+struct AzeritePowerLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "SpellID" },
+ { true, FT_INT, "ItemBonusListID" },
+ { true, FT_INT, "SpecSetID" },
+ { true, FT_INT, "Flags" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, AzeritePowerMeta::Instance(), HOTFIX_SEL_AZERITE_POWER);
+ return &loadInfo;
+ }
+};
+
struct BankBagSlotPricesLoadInfo
{
static DB2LoadInfo const* Instance()
@@ -4229,6 +4300,21 @@ struct SpecializationSpellsLoadInfo
}
};
+struct SpecSetMemberLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "ChrSpecializationID" },
+ { true, FT_INT, "SpecSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, SpecSetMemberMeta::Instance(), HOTFIX_SEL_SPEC_SET_MEMBER);
+ return &loadInfo;
+ }
+};
+
struct SpellAuraOptionsLoadInfo
{
static DB2LoadInfo const* Instance()
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 955347006cc..1d2df455f4c 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -55,9 +55,13 @@ 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<AzeriteEssenceEntry> sAzeriteEssenceStore("AzeriteEssence.db2", AzeriteEssenceLoadInfo::Instance());
+DB2Storage<AzeriteEssencePowerEntry> sAzeriteEssencePowerStore("AzeriteEssencePower.db2", AzeriteEssencePowerLoadInfo::Instance());
DB2Storage<AzeriteItemEntry> sAzeriteItemStore("AzeriteItem.db2", AzeriteItemLoadInfo::Instance());
+DB2Storage<AzeriteItemMilestonePowerEntry> sAzeriteItemMilestonePowerStore("AzeriteItemMilestonePower.db2", AzeriteItemMilestonePowerLoadInfo::Instance());
DB2Storage<AzeriteKnowledgeMultiplierEntry> sAzeriteKnowledgeMultiplierStore("AzeriteKnowledgeMultiplier.db2", AzeriteKnowledgeMultiplierLoadInfo::Instance());
DB2Storage<AzeriteLevelInfoEntry> sAzeriteLevelInfoStore("AzeriteLevelInfo.db2", AzeriteLevelInfoLoadInfo::Instance());
+DB2Storage<AzeritePowerEntry> sAzeritePowerStore("AzeritePower.db2", AzeritePowerLoadInfo::Instance());
DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore("BankBagSlotPrices.db2", BankBagSlotPricesLoadInfo::Instance());
DB2Storage<BannedAddonsEntry> sBannedAddonsStore("BannedAddons.db2", BannedAddonsLoadInfo::Instance());
DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore("BarberShopStyle.db2", BarberShopStyleLoadInfo::Instance());
@@ -219,6 +223,7 @@ DB2Storage<SkillLineAbilityEntry> sSkillLineAbilityStore("SkillLin
DB2Storage<SkillRaceClassInfoEntry> sSkillRaceClassInfoStore("SkillRaceClassInfo.db2", SkillRaceClassInfoLoadInfo::Instance());
DB2Storage<SoundKitEntry> sSoundKitStore("SoundKit.db2", SoundKitLoadInfo::Instance());
DB2Storage<SpecializationSpellsEntry> sSpecializationSpellsStore("SpecializationSpells.db2", SpecializationSpellsLoadInfo::Instance());
+DB2Storage<SpecSetMemberEntry> sSpecSetMemberStore("SpecSetMember.db2", SpecSetMemberLoadInfo::Instance());
DB2Storage<SpellAuraOptionsEntry> sSpellAuraOptionsStore("SpellAuraOptions.db2", SpellAuraOptionsLoadInfo::Instance());
DB2Storage<SpellAuraRestrictionsEntry> sSpellAuraRestrictionsStore("SpellAuraRestrictions.db2", SpellAuraRestrictionsLoadInfo::Instance());
DB2Storage<SpellCastTimesEntry> sSpellCastTimesStore("SpellCastTimes.db2", SpellCastTimesLoadInfo::Instance());
@@ -355,6 +360,9 @@ namespace
ArtifactPowersContainer _artifactPowers;
ArtifactPowerLinksContainer _artifactPowerLinks;
ArtifactPowerRanksContainer _artifactPowerRanks;
+ std::unordered_map<std::pair<uint32, uint32>, AzeriteEssencePowerEntry const*> _azeriteEssencePowersByIdAndRank;
+ std::vector<AzeriteItemMilestonePowerEntry const*> _azeriteItemMilestonePowers;
+ std::array<AzeriteItemMilestonePowerEntry const*, MAX_AZERITE_ESSENCE_SLOT> _azeriteItemMilestonePowerByEssenceSlot;
std::set<std::tuple<uint8, uint8, uint32>> _characterFacialHairStyles;
std::multimap<std::tuple<uint8, uint8, CharBaseSectionVariation>, CharSectionsEntry const*> _charSections;
CharStartOutfitContainer _charStartOutfits;
@@ -398,6 +406,7 @@ namespace
std::unordered_map<uint32, std::vector<SkillLineAbilityEntry const*>> _skillLineAbilitiesBySkillupSkill;
SkillRaceClassInfoContainer _skillRaceClassInfoBySkill;
SpecializationSpellsContainer _specializationSpellsBySpec;
+ std::unordered_set<std::pair<int32, uint32>> _specsBySpecSet;
std::unordered_set<uint8> _spellFamilyNames;
SpellPowerContainer _spellPowers;
SpellPowerDifficultyContainer _spellPowerDifficulties;
@@ -513,9 +522,13 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sArtifactTierStore);
LOAD_DB2(sArtifactUnlockStore);
LOAD_DB2(sAuctionHouseStore);
+ LOAD_DB2(sAzeriteEssenceStore);
+ LOAD_DB2(sAzeriteEssencePowerStore);
LOAD_DB2(sAzeriteItemStore);
+ LOAD_DB2(sAzeriteItemMilestonePowerStore);
LOAD_DB2(sAzeriteKnowledgeMultiplierStore);
LOAD_DB2(sAzeriteLevelInfoStore);
+ LOAD_DB2(sAzeritePowerStore);
LOAD_DB2(sBankBagSlotPricesStore);
LOAD_DB2(sBannedAddonsStore);
LOAD_DB2(sBarberShopStyleStore);
@@ -677,6 +690,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sSkillRaceClassInfoStore);
LOAD_DB2(sSoundKitStore);
LOAD_DB2(sSpecializationSpellsStore);
+ LOAD_DB2(sSpecSetMemberStore);
LOAD_DB2(sSpellAuraOptionsStore);
LOAD_DB2(sSpellAuraRestrictionsStore);
LOAD_DB2(sSpellCastTimesStore);
@@ -752,6 +766,31 @@ 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 (AzeriteEssencePowerEntry const* azeriteEssencePower : sAzeriteEssencePowerStore)
+ _azeriteEssencePowersByIdAndRank[std::pair<uint32, uint32>{ azeriteEssencePower->AzeriteEssenceID, azeriteEssencePower->Tier }] = azeriteEssencePower;
+
+ for (AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower : sAzeriteItemMilestonePowerStore)
+ _azeriteItemMilestonePowers.push_back(azeriteItemMilestonePower);
+
+ std::sort(_azeriteItemMilestonePowers.begin(), _azeriteItemMilestonePowers.end(), [](AzeriteItemMilestonePowerEntry const* a1, AzeriteItemMilestonePowerEntry const* a2)
+ {
+ return a1->RequiredLevel < a2->RequiredLevel;
+ });
+
+ {
+ uint32 azeriteEssenceSlot = 0;
+ for (AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower : _azeriteItemMilestonePowers)
+ {
+ AzeriteItemMilestoneType type = AzeriteItemMilestoneType(azeriteItemMilestonePower->Type);
+ if (type == AzeriteItemMilestoneType::MajorEssence || type == AzeriteItemMilestoneType::MinorEssence)
+ {
+ ASSERT(azeriteEssenceSlot < MAX_AZERITE_ESSENCE_SLOT);
+ _azeriteItemMilestonePowerByEssenceSlot[azeriteEssenceSlot] = azeriteItemMilestonePower;
+ ++azeriteEssenceSlot;
+ }
+ }
+ }
+
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());
@@ -1042,6 +1081,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
for (SpecializationSpellsEntry const* specSpells : sSpecializationSpellsStore)
_specializationSpellsBySpec[specSpells->SpecID].push_back(specSpells);
+ for (SpecSetMemberEntry const* specSetMember : sSpecSetMemberStore)
+ _specsBySpecSet.insert(std::make_pair(specSetMember->SpecSetID, uint32(specSetMember->ChrSpecializationID)));
+
for (SpellClassOptionsEntry const* classOption : sSpellClassOptionsStore)
_spellFamilyNames.insert(classOption->SpellClassSet);
@@ -1447,6 +1489,22 @@ bool DB2Manager::IsAzeriteItem(uint32 itemId) const
[&](AzeriteItemEntry const* azeriteItem) { return azeriteItem->ItemID == int32(itemId); }) != sAzeriteItemStore.end();
}
+AzeriteEssencePowerEntry const* DB2Manager::GetAzeriteEssencePower(uint32 azeriteEssenceId, uint32 rank) const
+{
+ return Trinity::Containers::MapGetValuePtr(_azeriteEssencePowersByIdAndRank, std::make_pair(azeriteEssenceId, rank));
+}
+
+std::vector<AzeriteItemMilestonePowerEntry const*> const& DB2Manager::GetAzeriteItemMilestonePowers() const
+{
+ return _azeriteItemMilestonePowers;
+}
+
+AzeriteItemMilestonePowerEntry const* DB2Manager::GetAzeriteItemMilestonePower(uint8 slot) const
+{
+ ASSERT(slot < MAX_AZERITE_ESSENCE_SLOT, "Slot %u must be lower than MAX_AZERITE_ESSENCE_SLOT (%u)", uint32(slot), MAX_AZERITE_ESSENCE_SLOT);
+ return _azeriteItemMilestonePowerByEssenceSlot[slot];
+}
+
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'))
@@ -2388,6 +2446,11 @@ std::vector<SpecializationSpellsEntry const*> const* DB2Manager::GetSpecializati
return nullptr;
}
+bool DB2Manager::IsSpecSetMember(int32 specSetId, uint32 specId) const
+{
+ return _specsBySpecSet.count(std::make_pair(specSetId, specId)) > 0;
+}
+
bool DB2Manager::IsValidSpellFamiliyName(SpellFamilyNames family)
{
return _spellFamilyNames.count(family) > 0;
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 2eb886e893a..b74a87ba11e 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -49,8 +49,11 @@ TC_GAME_API extern DB2Storage<ArtifactPowerPickerEntry> sArtifactPow
TC_GAME_API extern DB2Storage<ArtifactTierEntry> sArtifactTierStore;
TC_GAME_API extern DB2Storage<ArtifactUnlockEntry> sArtifactUnlockStore;
TC_GAME_API extern DB2Storage<AuctionHouseEntry> sAuctionHouseStore;
+TC_GAME_API extern DB2Storage<AzeriteEssenceEntry> sAzeriteEssenceStore;
TC_GAME_API extern DB2Storage<AzeriteKnowledgeMultiplierEntry> sAzeriteKnowledgeMultiplierStore;
+TC_GAME_API extern DB2Storage<AzeriteItemMilestonePowerEntry> sAzeriteItemMilestonePowerStore;
TC_GAME_API extern DB2Storage<AzeriteLevelInfoEntry> sAzeriteLevelInfoStore;
+TC_GAME_API extern DB2Storage<AzeritePowerEntry> sAzeritePowerStore;
TC_GAME_API extern DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore;
TC_GAME_API extern DB2Storage<BannedAddonsEntry> sBannedAddonsStore;
TC_GAME_API extern DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore;
@@ -272,6 +275,9 @@ public:
std::unordered_set<uint32> const* GetArtifactPowerLinks(uint32 artifactPowerId) const;
ArtifactPowerRankEntry const* GetArtifactPowerRank(uint32 artifactPowerId, uint8 rank) 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;
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;
@@ -333,6 +339,7 @@ public:
std::vector<SkillLineAbilityEntry const*> const* GetSkillLineAbilitiesBySkill(uint32 skillId) const;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const;
+ bool IsSpecSetMember(int32 specSetId, uint32 specId) const;
static bool IsValidSpellFamiliyName(SpellFamilyNames family);
std::vector<SpellPowerEntry const*> GetSpellPowers(uint32 spellId, Difficulty difficulty = DIFFICULTY_NONE, bool* hasDifficultyPowers = nullptr) const;
std::vector<SpellProcsPerMinuteModEntry const*> GetSpellProcsPerMinuteMods(uint32 spellprocsPerMinuteId) const;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 37a15c7a443..494919ceabb 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -259,12 +259,42 @@ struct AuctionHouseEntry
uint8 ConsignmentRate;
};
+struct AzeriteEssenceEntry
+{
+ LocalizedString* Name;
+ LocalizedString* Description;
+ uint32 ID;
+ int32 SpecSetID;
+};
+
+struct AzeriteEssencePowerEntry
+{
+ uint32 ID;
+ LocalizedString* SourceAlliance;
+ LocalizedString* SourceHorde;
+ int32 AzeriteEssenceID;
+ uint8 Tier;
+ int32 MajorPowerDescription;
+ int32 MinorPowerDescription;
+ int32 MajorPowerActual;
+ int32 MinorPowerActual;
+};
+
struct AzeriteItemEntry
{
uint32 ID;
int32 ItemID;
};
+struct AzeriteItemMilestonePowerEntry
+{
+ uint32 ID;
+ int32 RequiredLevel;
+ int32 AzeritePowerID;
+ int32 Type;
+ int32 AutoUnlock;
+};
+
struct AzeriteKnowledgeMultiplierEntry
{
uint32 ID;
@@ -279,6 +309,15 @@ struct AzeriteLevelInfoEntry
int32 ItemLevel;
};
+struct AzeritePowerEntry
+{
+ uint32 ID;
+ int32 SpellID;
+ int32 ItemBonusListID;
+ int32 SpecSetID;
+ int32 Flags;
+};
+
struct BankBagSlotPricesEntry
{
uint32 ID;
@@ -2512,6 +2551,13 @@ struct SpecializationSpellsEntry
uint8 DisplayOrder;
};
+struct SpecSetMemberEntry
+{
+ uint32 ID;
+ int32 ChrSpecializationID;
+ int32 SpecSetID;
+};
+
struct SpellAuraOptionsEntry
{
uint32 ID;
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index 9c760440ccc..15e82a1bf16 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -166,6 +166,16 @@ enum ArtifactPowerFlag : uint8
#define MAX_ARTIFACT_TIER 1
+#define MAX_AZERITE_ESSENCE_SLOT 3
+#define MAX_AZERITE_ESSENCE_RANK 4
+
+enum class AzeriteItemMilestoneType : int32
+{
+ MajorEssence = 0,
+ MinorEssence = 1,
+ BonusStamina = 2
+};
+
#define BATTLE_PET_SPECIES_MAX_ID 2796
enum ChrSpecializationFlag
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index db212bf76d5..7266be71202 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -18,6 +18,8 @@
#include "GameObject.h"
#include "ArtifactPackets.h"
+#include "AzeriteItem.h"
+#include "AzeritePackets.h"
#include "Battleground.h"
#include "CellImpl.h"
#include "CreatureAISelector.h"
@@ -1983,18 +1985,39 @@ void GameObject::Use(Unit* user)
if (!sConditionMgr->IsPlayerMeetingCondition(player, playerCondition))
return;
- Aura const* artifactAura = player->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE);
- Item const* item = artifactAura ? player->GetItemByGuid(artifactAura->GetCastItemGUID()) : nullptr;
- if (!item)
+ switch (info->itemForge.ForgeType)
{
- player->SendDirectMessage(WorldPackets::Misc::DisplayGameError(GameError::ERR_MUST_EQUIP_ARTIFACT).Write());
- return;
- }
+ case 0: // Artifact Forge
+ case 1: // Relic Forge
+ {
+ Aura const* artifactAura = player->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE);
+ Item const* item = artifactAura ? player->GetItemByGuid(artifactAura->GetCastItemGUID()) : nullptr;
+ if (!item)
+ {
+ player->SendDirectMessage(WorldPackets::Misc::DisplayGameError(GameError::ERR_MUST_EQUIP_ARTIFACT).Write());
+ return;
+ }
+
+ WorldPackets::Artifact::ArtifactForgeOpened artifactForgeOpened;
+ artifactForgeOpened.ArtifactGUID = item->GetGUID();
+ artifactForgeOpened.ForgeGUID = GetGUID();
+ player->SendDirectMessage(artifactForgeOpened.Write());
+ break;
+ }
+ case 2: // Heart Forge
+ {
+ Item const* item = player->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH);
+ if (!item)
+ return;
- WorldPackets::Artifact::ArtifactForgeOpened artifactForgeOpened;
- artifactForgeOpened.ArtifactGUID = item->GetGUID();
- artifactForgeOpened.ForgeGUID = GetGUID();
- player->SendDirectMessage(artifactForgeOpened.Write());
+ WorldPackets::Azerite::AzeriteEssenceForgeOpened azeriteEssenceForgeOpened;
+ azeriteEssenceForgeOpened.ForgeGUID = GetGUID();
+ player->SendDirectMessage(azeriteEssenceForgeOpened.Write());
+ break;
+ }
+ default:
+ break;
+ }
return;
}
case GAMEOBJECT_TYPE_UI_LINK:
diff --git a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp
index 8d9367d839a..1eb600e5722 100644
--- a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp
+++ b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp
@@ -19,6 +19,7 @@
#include "AzeritePackets.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
+#include "GameObject.h"
#include "GameTime.h"
#include "Player.h"
#include <boost/date_time/gregorian/gregorian_types.hpp>
@@ -39,6 +40,7 @@ bool AzeriteItem::Create(ObjectGuid::LowType guidlow, uint32 itemId, Player cons
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), 1);
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::KnowledgeLevel), GetCurrentKnowledgeLevel());
+ UnlockDefaultMilestones();
return true;
}
@@ -55,10 +57,49 @@ void AzeriteItem::SaveToDB(CharacterDatabaseTransaction& trans)
stmt->setUInt64(1, m_azeriteItemData->Xp);
stmt->setUInt32(2, m_azeriteItemData->Level);
stmt->setUInt32(3, m_azeriteItemData->KnowledgeLevel);
+ std::size_t specIndex = 0;
+ for (; specIndex < m_azeriteItemData->SelectedEssences.size(); ++specIndex)
+ {
+ stmt->setUInt32(4 + specIndex * 4, m_azeriteItemData->SelectedEssences[specIndex].SpecializationID);
+ for (std::size_t j = 0; j < MAX_AZERITE_ESSENCE_SLOT; ++j)
+ stmt->setUInt32(5 + specIndex * 4 + j, m_azeriteItemData->SelectedEssences[specIndex].AzeriteEssenceID[j]);
+ }
+ for (; specIndex < MAX_SPECIALIZATIONS; ++specIndex)
+ {
+ stmt->setUInt32(4 + specIndex * 4, 0);
+ for (std::size_t j = 0; j < MAX_AZERITE_ESSENCE_SLOT; ++j)
+ stmt->setUInt32(5 + specIndex * 4 + j, 0);
+ }
+
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ for (uint32 azeriteItemMilestonePowerId : m_azeriteItemData->UnlockedEssenceMilestones)
+ {
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_MILESTONE_POWER);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ stmt->setUInt32(1, azeriteItemMilestonePowerId);
+ trans->Append(stmt);
+ }
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE);
+ stmt->setUInt64(0, GetGUID().GetCounter());
trans->Append(stmt);
+
+ for (UF::UnlockedAzeriteEssence const& azeriteEssence : m_azeriteItemData->UnlockedEssences)
+ {
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ stmt->setUInt32(1, azeriteEssence.AzeriteEssenceID);
+ stmt->setUInt32(2, azeriteEssence.Rank);
+ trans->Append(stmt);
+ }
}
-void AzeriteItem::LoadAzeriteItemData(AzeriteItemData& azeriteItemData)
+void AzeriteItem::LoadAzeriteItemData(Player* owner, AzeriteItemData& azeriteItemData)
{
bool needSave = false;
@@ -94,6 +135,37 @@ void AzeriteItem::LoadAzeriteItemData(AzeriteItemData& azeriteItemData)
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Xp), azeriteItemData.Xp);
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), azeriteItemData.Level);
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::KnowledgeLevel), azeriteItemData.KnowledgeLevel);
+ for (uint32 azeriteItemMilestonePowerId : azeriteItemData.AzeriteItemMilestonePowers)
+ AddUnlockedEssenceMilestone(azeriteItemMilestonePowerId);
+
+ UnlockDefaultMilestones();
+
+ for (AzeriteEssencePowerEntry const* unlockedAzeriteEssence : azeriteItemData.UnlockedAzeriteEssences)
+ SetEssenceRank(unlockedAzeriteEssence->AzeriteEssenceID, unlockedAzeriteEssence->Tier);
+
+ for (AzeriteItemSelectedEssencesData const& selectedEssenceData : azeriteItemData.SelectedAzeriteEssences)
+ {
+ if (!selectedEssenceData.SpecializationId)
+ continue;
+
+ auto selectedEssences = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences));
+ selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::SpecializationID).SetValue(selectedEssenceData.SpecializationId);
+ for (uint32 i = 0; i < MAX_AZERITE_ESSENCE_SLOT; ++i)
+ {
+ // Check if essence was unlocked
+ if (!GetEssenceRank(selectedEssenceData.AzeriteEssenceId[i]))
+ continue;
+
+ selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::AzeriteEssenceID, i).SetValue(selectedEssenceData.AzeriteEssenceId[i]);
+ }
+
+ if (owner->GetPrimarySpecialization() == selectedEssenceData.SpecializationId)
+ selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::Enabled).SetValue(1);
+ }
+
+ // add selected essences for current spec
+ if (!GetSelectedAzeriteEssences())
+ CreateSelectedAzeriteEssences(owner->GetPrimarySpecialization());
if (needSave)
{
@@ -111,12 +183,15 @@ void AzeriteItem::DeleteFromDB(CharacterDatabaseTransaction& trans)
stmt->setUInt64(0, GetGUID().GetCounter());
trans->Append(stmt);
- Item::DeleteFromDB(trans);
-}
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
-uint32 AzeriteItem::GetItemLevel(Player const* /*owner*/) const
-{
- return sAzeriteLevelInfoStore.AssertEntry(m_azeriteItemData->Level)->ItemLevel;
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ Item::DeleteFromDB(trans);
}
uint32 AzeriteItem::GetCurrentKnowledgeLevel()
@@ -176,6 +251,7 @@ void AzeriteItem::GiveXP(uint64 xp)
owner->_ApplyItemBonuses(this, GetSlot(), false);
SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), level);
+ UnlockDefaultMilestones();
owner->UpdateCriteria(CRITERIA_TYPE_HEART_OF_AZEROTH_LEVEL_REACHED, level);
if (IsEquipped())
@@ -191,6 +267,70 @@ void AzeriteItem::GiveXP(uint64 xp)
owner->SendDirectMessage(xpGain.Write());
}
+GameObject const* AzeriteItem::FindHeartForge(Player const* owner)
+{
+ if (GameObject const* forge = owner->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_ITEM_FORGE, 40.0f))
+ if (forge->GetGOInfo()->itemForge.ForgeType == 2)
+ return forge;
+
+ return nullptr;
+}
+
+bool AzeriteItem::CanUseEssences() const
+{
+ if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(PLAYER_CONDITION_ID_UNLOCKED_AZERITE_ESSENCES))
+ return ConditionMgr::IsPlayerMeetingCondition(GetOwner(), condition);
+
+ return false;
+}
+
+bool AzeriteItem::HasUnlockedEssenceSlot(uint8 slot) const
+{
+ AzeriteItemMilestonePowerEntry const* milestone = sDB2Manager.GetAzeriteItemMilestonePower(slot);
+ return m_azeriteItemData->UnlockedEssenceMilestones.FindIndex(milestone->ID) != -1;
+}
+
+uint32 AzeriteItem::GetEssenceRank(uint32 azeriteEssenceId) const
+{
+ int32 index = m_azeriteItemData->UnlockedEssences.FindIndexIf([azeriteEssenceId](UF::UnlockedAzeriteEssence const& essence)
+ {
+ return essence.AzeriteEssenceID == azeriteEssenceId;
+ });
+
+ if (index < 0)
+ return 0;
+
+ return m_azeriteItemData->UnlockedEssences[index].Rank;
+}
+
+void AzeriteItem::SetEssenceRank(uint32 azeriteEssenceId, uint32 rank)
+{
+ int32 index = m_azeriteItemData->UnlockedEssences.FindIndexIf([azeriteEssenceId](UF::UnlockedAzeriteEssence const& essence)
+ {
+ return essence.AzeriteEssenceID == azeriteEssenceId;
+ });
+
+ if (!rank && index >= 0)
+ {
+ RemoveDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::UnlockedEssences), index);
+ return;
+ }
+
+ if (!sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, rank))
+ return;
+
+ if (index < 0)
+ {
+ UF::UnlockedAzeriteEssence& unlockedEssence = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData)
+ .ModifyValue(&UF::AzeriteItemData::UnlockedEssences));
+ unlockedEssence.AzeriteEssenceID = azeriteEssenceId;
+ unlockedEssence.Rank = rank;
+ }
+ else
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::UnlockedEssences, index)
+ .ModifyValue(&UF::UnlockedAzeriteEssence::Rank), rank);
+}
+
UF::SelectedAzeriteEssences const* AzeriteItem::GetSelectedAzeriteEssences() const
{
for (UF::SelectedAzeriteEssences const& essences : m_azeriteItemData->SelectedEssences)
@@ -200,6 +340,34 @@ UF::SelectedAzeriteEssences const* AzeriteItem::GetSelectedAzeriteEssences() con
return nullptr;
}
+void AzeriteItem::SetSelectedAzeriteEssences(uint32 specializationId)
+{
+ int32 index = m_azeriteItemData->SelectedEssences.FindIndexIf([](UF::SelectedAzeriteEssences const& essences) { return essences.Enabled == 1; });
+ if (index >= 0)
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index)
+ .ModifyValue(&UF::SelectedAzeriteEssences::Enabled), 0);
+
+ index = m_azeriteItemData->SelectedEssences.FindIndexIf([specializationId](UF::SelectedAzeriteEssences const& essences)
+ {
+ return essences.SpecializationID == specializationId;
+ });
+
+ if (index >= 0)
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index)
+ .ModifyValue(&UF::SelectedAzeriteEssences::Enabled), 1);
+ else
+ CreateSelectedAzeriteEssences(specializationId);
+}
+
+void AzeriteItem::SetSelectedAzeriteEssence(uint8 slot, uint32 azeriteEssenceId)
+{
+ ASSERT(slot < MAX_AZERITE_ESSENCE_SLOT);
+ int32 index = m_azeriteItemData->SelectedEssences.FindIndexIf([](UF::SelectedAzeriteEssences const& essences) { return essences.Enabled == 1; });
+ ASSERT(index >= 0);
+ SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index)
+ .ModifyValue(&UF::SelectedAzeriteEssences::AzeriteEssenceID, slot), azeriteEssenceId);
+}
+
void AzeriteItem::BuildValuesCreate(ByteBuffer* data, Player const* target) const
{
UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target);
@@ -257,3 +425,34 @@ void AzeriteItem::ClearUpdateMask(bool remove)
m_values.ClearChangesMask(&AzeriteItem::m_azeriteItemData);
Item::ClearUpdateMask(remove);
}
+
+void AzeriteItem::UnlockDefaultMilestones()
+{
+ bool hasPreviousMilestone = true;
+ for (AzeriteItemMilestonePowerEntry const* milestone : sDB2Manager.GetAzeriteItemMilestonePowers())
+ {
+ if (!hasPreviousMilestone)
+ break;
+
+ if (milestone->RequiredLevel > int32(GetLevel()))
+ break;
+
+ if (HasUnlockedEssenceMilestone(milestone->ID))
+ continue;
+
+ if (milestone->AutoUnlock)
+ {
+ AddUnlockedEssenceMilestone(milestone->ID);
+ hasPreviousMilestone = true;
+ }
+ else
+ hasPreviousMilestone = false;
+ }
+}
+
+void AzeriteItem::CreateSelectedAzeriteEssences(uint32 specializationId)
+{
+ auto selectedEssences = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences));
+ selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::SpecializationID).SetValue(specializationId);
+ selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::Enabled).SetValue(1);
+}
diff --git a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.h b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.h
index 6c08ab57f9e..830b71b9a98 100644
--- a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.h
+++ b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.h
@@ -23,6 +23,8 @@
constexpr uint32 ITEM_ID_HEART_OF_AZEROTH = 158075;
constexpr uint32 MAX_AZERITE_ITEM_LEVEL = 70;
constexpr uint32 MAX_AZERITE_ITEM_KNOWLEDGE_LEVEL = 30;
+constexpr uint32 PLAYER_CONDITION_ID_UNLOCKED_AZERITE_ESSENCES = 69048;
+constexpr uint32 SPELL_ID_HEART_ESSENCE_ACTION_BAR_OVERRIDE = 298554;
class TC_GAME_API AzeriteItem : public Item
{
@@ -32,11 +34,9 @@ public:
bool Create(ObjectGuid::LowType guidlow, uint32 itemId, Player const* owner) override;
void SaveToDB(CharacterDatabaseTransaction& trans) override;
- void LoadAzeriteItemData(AzeriteItemData& azeriteItem);
+ void LoadAzeriteItemData(Player* owner, AzeriteItemData& azeriteItem);
void DeleteFromDB(CharacterDatabaseTransaction& trans) override;
- uint32 GetItemLevel(Player const* owner) const override;
-
uint32 GetLevel() const { return m_azeriteItemData->Level; }
uint32 GetEffectiveLevel() const
{
@@ -47,11 +47,29 @@ public:
return level;
}
+ // Gaining artifact power
static uint32 GetCurrentKnowledgeLevel();
static uint64 CalcTotalXPToNextLevel(uint32 level, uint32 knowledgeLevel);
void GiveXP(uint64 xp);
+ // Essences
+ // C_AzeriteEssence.CanOpenUI - checks PlayerCondition 69048 - HasAura(261912) || RewardedQuest(57010) || IsOnQuest(57010)
+ static GameObject const* FindHeartForge(Player const* owner);
+ bool CanUseEssences() const;
+ bool HasUnlockedEssenceSlot(uint8 slot) const;
+ bool HasUnlockedEssenceMilestone(uint32 azeriteItemMilestonePowerId) const { return m_azeriteItemData->UnlockedEssenceMilestones.FindIndex(azeriteItemMilestonePowerId) != -1; }
+ void AddUnlockedEssenceMilestone(uint32 azeriteItemMilestonePowerId)
+ {
+ AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData)
+ .ModifyValue(&UF::AzeriteItemData::UnlockedEssenceMilestones)) = azeriteItemMilestonePowerId;
+ }
+
+ uint32 GetEssenceRank(uint32 azeriteEssenceId) const;
+ void SetEssenceRank(uint32 azeriteEssenceId, uint32 rank);
+
UF::SelectedAzeriteEssences const* GetSelectedAzeriteEssences() const;
+ void SetSelectedAzeriteEssences(uint32 specializationId);
+ void SetSelectedAzeriteEssence(uint8 slot, uint32 azeriteEssenceId);
void BuildValuesCreate(ByteBuffer* data, Player const* target) const override;
void BuildValuesUpdate(ByteBuffer* data, Player const* target) const override;
@@ -59,6 +77,10 @@ public:
void ClearUpdateMask(bool remove) override;
UF::UpdateField<UF::AzeriteItemData, 0, TYPEID_AZERITE_ITEM> m_azeriteItemData;
+
+private:
+ void UnlockDefaultMilestones();
+ void CreateSelectedAzeriteEssences(uint32 specializationId);
};
#endif // AzeriteItem_h__
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index f0ec7dbd383..c702be312db 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -284,7 +284,8 @@ static uint32 const IllusionModifierMaskSpecSpecific =
(1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4);
void ItemAdditionalLoadInfo::Init(std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo>* loadInfo,
- PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult)
+ PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult,
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult)
{
// 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 ...
@@ -321,7 +322,16 @@ void ItemAdditionalLoadInfo::Init(std::unordered_map<ObjectGuid::LowType, ItemAd
}
// 0 1 2 3
- // SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel FROM item_instance_azerite iz INNER JOIN ...
+ // SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel,
+ // 4 5 6 7
+ // iz.selectedAzeriteEssences1specId, iz.selectedAzeriteEssences1azeriteEssenceId1, iz.selectedAzeriteEssences1azeriteEssenceId2, iz.selectedAzeriteEssences1azeriteEssenceId3,
+ // 8 9 10 11
+ // iz.selectedAzeriteEssences2specId, iz.selectedAzeriteEssences2azeriteEssenceId1, iz.selectedAzeriteEssences2azeriteEssenceId2, iz.selectedAzeriteEssences2azeriteEssenceId3,
+ // 12 13 14 15
+ // iz.selectedAzeriteEssences3specId, iz.selectedAzeriteEssences3azeriteEssenceId1, iz.selectedAzeriteEssences3azeriteEssenceId2, iz.selectedAzeriteEssences3azeriteEssenceId3,
+ // 16 17 18 19
+ // iz.selectedAzeriteEssences4specId, iz.selectedAzeriteEssences4azeriteEssenceId1, iz.selectedAzeriteEssences4azeriteEssenceId2, iz.selectedAzeriteEssences4azeriteEssenceId3
+ // FROM item_instance_azerite iz INNER JOIN ...
if (azeriteItemResult)
{
do
@@ -333,9 +343,59 @@ void ItemAdditionalLoadInfo::Init(std::unordered_map<ObjectGuid::LowType, ItemAd
info.AzeriteItem->Xp = fields[1].GetUInt64();
info.AzeriteItem->Level = fields[2].GetUInt32();
info.AzeriteItem->KnowledgeLevel = fields[3].GetUInt32();
+ for (std::size_t i = 0; i < MAX_SPECIALIZATIONS; ++i)
+ {
+ uint32 specializationId = fields[4 + i * 4].GetUInt32();
+ if (!sChrSpecializationStore.LookupEntry(specializationId))
+ continue;
+
+ info.AzeriteItem->SelectedAzeriteEssences[i].SpecializationId = specializationId;
+ for (std::size_t j = 0; j < MAX_AZERITE_ESSENCE_SLOT; ++j)
+ {
+ AzeriteEssenceEntry const* azeriteEssence = sAzeriteEssenceStore.LookupEntry(fields[5 + i * 4 + j].GetUInt32());
+ if (!azeriteEssence || !sDB2Manager.IsSpecSetMember(azeriteEssence->SpecSetID, specializationId))
+ continue;
+
+ info.AzeriteItem->SelectedAzeriteEssences[i].AzeriteEssenceId[j] = azeriteEssence->ID;
+ }
+ }
} while (azeriteItemResult->NextRow());
}
+
+ // 0 1
+ // SELECT iamp.itemGuid, iamp.azeriteItemMilestonePowerId FROM item_instance_azerite_milestone_power iamp INNER JOIN ...
+ if (azeriteItemMilestonePowersResult)
+ {
+ do
+ {
+ Field* fields = azeriteItemMilestonePowersResult->Fetch();
+ ItemAdditionalLoadInfo& info = (*loadInfo)[fields[0].GetUInt64()];
+ if (!info.AzeriteItem)
+ info.AzeriteItem = boost::in_place();
+ info.AzeriteItem->AzeriteItemMilestonePowers.push_back(fields[1].GetUInt32());
+ }
+ while (azeriteItemMilestonePowersResult->NextRow());
+ }
+
+ // 0 1 2
+ // SELECT iaue.itemGuid, iaue.azeriteEssenceId, iaue.`rank` FROM item_instance_azerite_unlocked_essence iaue INNER JOIN ...
+ if (azeriteItemUnlockedEssencesResult)
+ {
+ do
+ {
+ Field* fields = azeriteItemUnlockedEssencesResult->Fetch();
+ if (AzeriteEssencePowerEntry const* azeriteEssencePower = sDB2Manager.GetAzeriteEssencePower(fields[1].GetUInt32(), fields[2].GetUInt32()))
+ {
+ ItemAdditionalLoadInfo& info = (*loadInfo)[fields[0].GetUInt64()];
+ if (!info.AzeriteItem)
+ info.AzeriteItem = boost::in_place();
+
+ info.AzeriteItem->UnlockedAzeriteEssences.push_back(azeriteEssencePower);
+ }
+ }
+ while (azeriteItemUnlockedEssencesResult->NextRow());
+ }
}
Item::Item()
@@ -2180,21 +2240,28 @@ void Item::ItemContainerDeleteLootMoneyAndLootItemsFromDB()
uint32 Item::GetItemLevel(Player const* owner) const
{
+ ItemTemplate const* itemTemplate = GetTemplate();
uint32 minItemLevel = owner->m_unitData->MinItemLevel;
uint32 minItemLevelCutoff = owner->m_unitData->MinItemLevelCutoff;
- uint32 maxItemLevel = GetTemplate()->GetFlags3() & ITEM_FLAG3_IGNORE_ITEM_LEVEL_CAP_IN_PVP ? 0 : owner->m_unitData->MaxItemLevel;
+ uint32 maxItemLevel = itemTemplate->GetFlags3() & ITEM_FLAG3_IGNORE_ITEM_LEVEL_CAP_IN_PVP ? 0 : owner->m_unitData->MaxItemLevel;
bool pvpBonus = owner->IsUsingPvpItemLevels();
- return Item::GetItemLevel(GetTemplate(), _bonusData, owner->getLevel(), GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL),
- minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus);
+ uint32 azeriteLevel = 0;
+ if (AzeriteItem const* azeriteItem = ToAzeriteItem())
+ azeriteLevel = azeriteItem->GetEffectiveLevel();
+ return Item::GetItemLevel(itemTemplate, _bonusData, owner->getLevel(), GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL),
+ minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus, azeriteLevel);
}
uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bonusData, uint32 level, uint32 fixedLevel,
- uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus)
+ uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel)
{
if (!itemTemplate)
return MIN_ITEM_LEVEL;
uint32 itemLevel = itemTemplate->GetBaseItemLevel();
+ if (AzeriteLevelInfoEntry const* azeriteLevelInfo = sAzeriteLevelInfoStore.LookupEntry(azeriteLevel))
+ itemLevel = azeriteLevelInfo->ItemLevel;
+
if (ScalingStatDistributionEntry const* ssd = sScalingStatDistributionStore.LookupEntry(bonusData.ScalingStatDistribution))
{
if (fixedLevel)
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index e029bc0ee05..5b5cb12a569 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -110,29 +110,39 @@ private:
struct ArtifactPowerData
{
- uint32 ArtifactPowerId;
- uint8 PurchasedRank;
- uint8 CurrentRankWithBonus;
+ uint32 ArtifactPowerId = 0;
+ uint8 PurchasedRank = 0;
+ uint8 CurrentRankWithBonus = 0;
};
struct ArtifactData
{
- uint64 Xp;
- uint32 ArtifactAppearanceId;
- uint32 ArtifactTierId;
+ uint64 Xp = 0;
+ uint32 ArtifactAppearanceId = 0;
+ uint32 ArtifactTierId = 0;
std::vector<ArtifactPowerData> ArtifactPowers;
};
+struct AzeriteItemSelectedEssencesData
+{
+ uint32 SpecializationId = 0;
+ std::array<uint32, MAX_AZERITE_ESSENCE_SLOT> AzeriteEssenceId = { };
+};
+
struct AzeriteItemData
{
uint64 Xp;
uint32 Level;
uint32 KnowledgeLevel;
+ std::vector<uint32> AzeriteItemMilestonePowers;
+ std::vector<AzeriteEssencePowerEntry const*> UnlockedAzeriteEssences;
+ std::array<AzeriteItemSelectedEssencesData, MAX_SPECIALIZATIONS> SelectedAzeriteEssences = { };
};
struct ItemAdditionalLoadInfo
{
- static void Init(std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo>* loadInfo, PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult);
+ static void Init(std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo>* loadInfo, PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult,
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult);
Optional<ArtifactData> Artifact;
Optional<AzeriteItemData> AzeriteItem;
@@ -308,9 +318,9 @@ class TC_GAME_API Item : public Object
bool IsConjuredConsumable() const { return GetTemplate()->IsConjuredConsumable(); }
bool IsRangedWeapon() const { return GetTemplate()->IsRangedWeapon(); }
uint32 GetQuality() const { return _bonusData.Quality; }
- virtual uint32 GetItemLevel(Player const* owner) const;
+ uint32 GetItemLevel(Player const* owner) const;
static uint32 GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bonusData, uint32 level, uint32 fixedLevel,
- uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus);
+ uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel);
int32 GetRequiredLevel() const;
int32 GetItemStatType(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_STATS); return _bonusData.ItemStatType[index]; }
int32 GetItemStatValue(uint32 index, Player const* owner) const;
diff --git a/src/server/game/Entities/Item/ItemDefines.h b/src/server/game/Entities/Item/ItemDefines.h
index 3bb7eb7296a..0b34fe56b4a 100644
--- a/src/server/game/Entities/Item/ItemDefines.h
+++ b/src/server/game/Entities/Item/ItemDefines.h
@@ -217,4 +217,20 @@ enum ItemModifier : uint16
MAX_ITEM_MODIFIERS
};
+enum class AzeriteEssenceActivateResult : uint32
+{
+ None = 0,
+ EssenceNotUnlocked = 2, // Arg: AzeriteEssenceID
+ CantDoThatRightNow = 3,
+ AffectingCombat = 4,
+ CantRemoveEssence = 5, // Arg: SpellID of active essence on cooldown
+ ChallengeModeActive = 6,
+ NotInRestArea = 7,
+ ConditionFailed = 8,
+ SlotLocked = 9,
+ NotAtForge = 10,
+ HeartLevelTooLow = 11, // Arg: RequiredLevel
+ NotEquipped = 12
+};
+
#endif // ItemDefines_h__
diff --git a/src/server/game/Entities/Object/Updates/UpdateField.h b/src/server/game/Entities/Object/Updates/UpdateField.h
index f99960fa451..d7a279ada4d 100644
--- a/src/server/game/Entities/Object/Updates/UpdateField.h
+++ b/src/server/game/Entities/Object/Updates/UpdateField.h
@@ -582,6 +582,11 @@ namespace UF
return std::end(_values);
}
+ std::size_t size() const
+ {
+ return Size;
+ }
+
T const& operator[](uint32 index) const
{
return _values[index];
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
index 141f6f27317..04c2fd78b6e 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
@@ -470,7 +470,7 @@ void ContainerData::ClearChangesMask()
_changesMask.ResetAll();
}
-void AzeriteEmpoweredItemData::WriteCreate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, Item const* owner, Player const* receiver) const
+void AzeriteEmpoweredItemData::WriteCreate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, AzeriteEmpoweredItem const* owner, Player const* receiver) const
{
for (std::size_t i = 0; i < 5; ++i)
{
@@ -478,7 +478,7 @@ void AzeriteEmpoweredItemData::WriteCreate(ByteBuffer& data, EnumClassFlag<Updat
}
}
-void AzeriteEmpoweredItemData::WriteUpdate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, Item const* owner, Player const* receiver) const
+void AzeriteEmpoweredItemData::WriteUpdate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, AzeriteEmpoweredItem const* owner, Player const* receiver) const
{
UpdateMask<6> const& changesMask = _changesMask;
data.WriteBits(changesMask.GetBlocksMask(0), 1);
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h
index 75c15ad52d8..e6f207e5049 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.h
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.h
@@ -26,6 +26,7 @@
#include "UpdateMask.h"
class AreaTrigger;
+class AzeriteEmpoweredItem;
class AzeriteItem;
class Bag;
class ByteBuffer;
@@ -130,8 +131,8 @@ struct AzeriteEmpoweredItemData : public IsUpdateFieldStructureTag, public HasCh
{
UpdateFieldArray<int32, 5, 0, 1> Selections;
- void WriteCreate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, Item const* owner, Player const* receiver) const;
- void WriteUpdate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, Item const* owner, Player const* receiver) const;
+ void WriteCreate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, AzeriteEmpoweredItem const* owner, Player const* receiver) const;
+ void WriteUpdate(ByteBuffer& data, EnumClassFlag<UpdateFieldFlag> fieldVisibilityFlags, AzeriteEmpoweredItem const* owner, Player const* receiver) const;
void ClearChangesMask();
};
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 3b1eebbc10b..b5a96465e79 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -3225,7 +3225,7 @@ bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
return false;
}
-void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/)
+void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/, bool suppressMessaging /*= false*/)
{
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
@@ -3239,6 +3239,7 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/
{
WorldPackets::Spells::LearnedSpells packet;
packet.SpellID.push_back(spell_id);
+ packet.SuppressMessaging = suppressMessaging;
GetSession()->SendPacket(packet.Write());
}
@@ -3262,7 +3263,7 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/
}
}
-void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
+void Player::RemoveSpell(uint32 spell_id, bool disabled /*= false*/, bool learn_low_rank /*= true*/, bool suppressMessaging /*= false*/)
{
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end())
@@ -3430,6 +3431,7 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
{
WorldPackets::Spells::UnlearnedSpells unlearnedSpells;
unlearnedSpells.SpellID.push_back(spell_id);
+ unlearnedSpells.SuppressMessaging = suppressMessaging;
SendDirectMessage(unlearnedSpells.Write());
}
}
@@ -4053,6 +4055,14 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER_BY_OWNER);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE_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);
@@ -7325,6 +7335,7 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemA
if (updateItemAuras)
ApplyItemDependentAuras(item, apply);
ApplyArtifactPowers(item, apply);
+ ApplyAzeritePowers(item, apply);
ApplyEnchantment(item, apply);
TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: completed");
@@ -7910,6 +7921,90 @@ void Player::ApplyArtifactPowerRank(Item* artifact, ArtifactPowerRankEntry const
}
+void Player::ApplyAzeritePowers(Item* item, bool apply)
+{
+ if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
+ {
+ // milestone powers
+ for (uint32 azeriteItemMilestonePowerId : azeriteItem->m_azeriteItemData->UnlockedEssenceMilestones)
+ ApplyAzeriteItemMilestonePower(item, sAzeriteItemMilestonePowerStore.AssertEntry(azeriteItemMilestonePowerId), apply);
+
+ // essences
+ if (UF::SelectedAzeriteEssences const* selectedEssences = azeriteItem->GetSelectedAzeriteEssences())
+ for (uint8 slot = 0; slot < MAX_AZERITE_ESSENCE_SLOT; ++slot)
+ if (selectedEssences->AzeriteEssenceID[slot])
+ ApplyAzeriteEssence(item, selectedEssences->AzeriteEssenceID[slot], azeriteItem->GetEssenceRank(selectedEssences->AzeriteEssenceID[slot]),
+ AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(slot)->Type) == AzeriteItemMilestoneType::MajorEssence, apply);
+
+ return;
+ }
+}
+
+void Player::ApplyAzeriteItemMilestonePower(Item* item, AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower, bool apply)
+{
+ AzeriteItemMilestoneType type = AzeriteItemMilestoneType(azeriteItemMilestonePower->Type);
+ if (type == AzeriteItemMilestoneType::BonusStamina)
+ {
+ if (AzeritePowerEntry const* azeritePower = sAzeritePowerStore.LookupEntry(azeriteItemMilestonePower->AzeritePowerID))
+ {
+ if (apply)
+ CastSpell(this, azeritePower->SpellID, true, item);
+ else
+ RemoveAurasDueToItemSpell(azeritePower->SpellID, item->GetGUID());
+ }
+ }
+}
+
+void Player::ApplyAzeriteEssence(Item* item, uint32 azeriteEssenceId, uint32 rank, bool major, bool apply)
+{
+ for (uint32 currentRank = 1; currentRank <= rank; ++currentRank)
+ {
+ if (AzeriteEssencePowerEntry const* azeriteEssencePower = sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, currentRank))
+ {
+ ApplyAzeriteEssencePower(item, azeriteEssencePower, major, apply);
+ if (major && currentRank == 1)
+ {
+ if (apply)
+ CastCustomSpell(SPELL_ID_HEART_ESSENCE_ACTION_BAR_OVERRIDE, SPELLVALUE_BASE_POINT0, azeriteEssencePower->MajorPowerDescription, this, TRIGGERED_FULL_MASK);
+ else
+ RemoveAurasDueToSpell(SPELL_ID_HEART_ESSENCE_ACTION_BAR_OVERRIDE);
+ }
+ }
+ }
+}
+
+void Player::ApplyAzeriteEssencePower(Item* item, AzeriteEssencePowerEntry const* azeriteEssencePower, bool major, bool apply)
+{
+ if (SpellInfo const* powerSpell = sSpellMgr->GetSpellInfo(azeriteEssencePower->MinorPowerDescription))
+ {
+ if (apply)
+ CastSpell(this, powerSpell, true, item);
+ else
+ RemoveAurasDueToItemSpell(powerSpell->Id, item->GetGUID());
+ }
+
+ if (major)
+ {
+ if (SpellInfo const* powerSpell = sSpellMgr->GetSpellInfo(azeriteEssencePower->MajorPowerDescription))
+ {
+ if (powerSpell->IsPassive())
+ {
+ if (apply)
+ CastSpell(this, powerSpell, true, item);
+ else
+ RemoveAurasDueToItemSpell(powerSpell->Id, item->GetGUID());
+ }
+ else
+ {
+ if (apply)
+ LearnSpell(powerSpell->Id, true, 0, true);
+ else
+ RemoveSpell(powerSpell->Id, false, false, true);
+ }
+ }
+ }
+}
+
void Player::CastItemCombatSpell(DamageInfo const& damageInfo)
{
Unit* target = damageInfo.GetVictim();
@@ -8215,6 +8310,7 @@ void Player::_RemoveAllItemMods()
ApplyItemEquipSpell(m_items[i], false);
ApplyEnchantment(m_items[i], false);
+ ApplyAzeritePowers(m_items[i], false);
ApplyArtifactPowers(m_items[i], false);
}
}
@@ -8267,6 +8363,7 @@ void Player::_ApplyAllItemMods()
ApplyItemEquipSpell(m_items[i], true);
ApplyArtifactPowers(m_items[i], true);
+ ApplyAzeritePowers(m_items[i], true);
ApplyEnchantment(m_items[i], true);
}
}
@@ -18188,6 +18285,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder)
_LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY),
holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ARTIFACTS),
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),
time_diff);
if (IsVoidStorageUnlocked())
@@ -18585,7 +18684,8 @@ void Player::LoadCorpse(PreparedQueryResult result)
RemoveAtLoginFlag(AT_LOGIN_RESURRECT);
}
-void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult, uint32 timeDiff)
+void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult,
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, 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,
@@ -18616,7 +18716,7 @@ 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);
+ ItemAdditionalLoadInfo::Init(&additionalData, artifactsResult, azeriteResult, azeriteItemMilestonePowersResult, azeriteItemUnlockedEssencesResult);
if (result)
{
@@ -18642,7 +18742,7 @@ void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult arti
if (addionalDataPtr->AzeriteItem)
if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
- azeriteItem->LoadAzeriteItemData(*addionalDataPtr->AzeriteItem);
+ azeriteItem->LoadAzeriteItemData(this, *addionalDataPtr->AzeriteItem);
}
ObjectGuid bagGuid = fields[43].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[43].GetUInt64()) : ObjectGuid::Empty;
@@ -19000,7 +19100,7 @@ void Player::_LoadMailedItem(Mail* mail, Field* fields, ItemAdditionalLoadInfo*
if (addionalData->AzeriteItem)
if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
- azeriteItem->LoadAzeriteItemData(*addionalData->AzeriteItem);
+ azeriteItem->LoadAzeriteItemData(this, *addionalData->AzeriteItem);
}
AddMItem(item);
@@ -19078,8 +19178,16 @@ void Player::_LoadMail()
stmt->setUInt64(0, GetGUID().GetCounter());
PreparedQueryResult azeriteResult = CharacterDatabase.Query(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS_AZERITE_MILESTONE_POWER);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ PreparedQueryResult azeriteItemMilestonePowersResult = CharacterDatabase.Query(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS_AZERITE_UNLOCKED_ESSENCE);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ PreparedQueryResult azeriteItemUnlockedEssencesResult = CharacterDatabase.Query(stmt);
+
std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo> additionalData;
- ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult);
+ ItemAdditionalLoadInfo::Init(&additionalData, artifactResult, azeriteResult, azeriteItemMilestonePowersResult, azeriteItemUnlockedEssencesResult);
do
{
@@ -27070,6 +27178,22 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec)
activeGlyphs.IsFullUpdate = true;
SendDirectMessage(activeGlyphs.Write());
+ if (Item* item = GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH))
+ {
+ if (AzeriteItem* azeriteItem = item->ToAzeriteItem())
+ {
+ if (azeriteItem->IsEquipped())
+ ApplyAzeritePowers(azeriteItem, false);
+
+ azeriteItem->SetSelectedAzeriteEssences(spec->ID);
+
+ if (azeriteItem->IsEquipped())
+ ApplyAzeritePowers(azeriteItem, true);
+
+ azeriteItem->SetState(ITEM_CHANGED, this);
+ }
+ }
+
Unit::AuraEffectList const& shapeshiftAuras = GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT);
for (AuraEffect* aurEff : shapeshiftAuras)
{
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 07c2f7030c1..0c2131a3e9b 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -39,6 +39,8 @@ struct AchievementEntry;
struct AreaTableEntry;
struct AreaTriggerEntry;
struct ArtifactPowerRankEntry;
+struct AzeriteEssencePowerEntry;
+struct AzeriteItemMilestonePowerEntry;
struct BarberShopStyleEntry;
struct CharTitlesEntry;
struct ChatChannelsEntry;
@@ -751,6 +753,8 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_INVENTORY,
PLAYER_LOGIN_QUERY_LOAD_ARTIFACTS,
PLAYER_LOGIN_QUERY_LOAD_AZERITE,
+ PLAYER_LOGIN_QUERY_LOAD_AZERITE_MILESTONE_POWERS,
+ PLAYER_LOGIN_QUERY_LOAD_AZERITE_UNLOCKED_ESSENCES,
PLAYER_LOGIN_QUERY_LOAD_ACTIONS,
PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT,
PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE,
@@ -1543,8 +1547,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendProficiency(ItemClass itemClass, uint32 itemSubclassMask) const;
void SendKnownSpells();
bool AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, int32 fromSkill = 0);
- void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill = 0);
- void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true);
+ void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill = 0, bool suppressMessaging = false);
+ void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true, bool suppressMessaging = false);
void ResetSpells(bool myClassOnly = false);
void LearnCustomSpells();
void LearnDefaultSkills();
@@ -2012,6 +2016,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void UpdateItemSetAuras(bool formChange = false);
void ApplyArtifactPowers(Item* item, bool apply);
void ApplyArtifactPowerRank(Item* artifact, ArtifactPowerRankEntry const* artifactPowerRank, bool apply);
+ void ApplyAzeritePowers(Item* item, bool apply);
+ void ApplyAzeriteItemMilestonePower(Item* item, AzeriteItemMilestonePowerEntry const* azeriteItemMilestonePower, bool apply);
+ void ApplyAzeriteEssence(Item* item, uint32 azeriteEssenceId, uint32 rank, bool major, bool apply);
+ void ApplyAzeriteEssencePower(Item* item, AzeriteEssencePowerEntry const* azeriteEssencePower, bool major, bool apply);
void CastItemCombatSpell(DamageInfo const& damageInfo);
void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto);
@@ -2489,7 +2497,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effectResult, uint32 timediff);
void _LoadGlyphAuras();
void _LoadBoundInstances(PreparedQueryResult result);
- void _LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult, uint32 timeDiff);
+ void _LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, PreparedQueryResult azeriteResult,
+ PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, uint32 timeDiff);
void _LoadVoidStorage(PreparedQueryResult result);
void _LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery);
void _LoadMail();
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index eb3ff858060..dcbf16051e1 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -1913,7 +1913,7 @@ ItemDisenchantLootEntry const* Roll::GetItemDisenchantLoot(Player const* player)
bonusData.Initialize(itemInstance);
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemid);
- uint32 itemLevel = Item::GetItemLevel(itemTemplate, bonusData, player->getLevel(), 0, 0, 0, 0, false);
+ uint32 itemLevel = Item::GetItemLevel(itemTemplate, bonusData, player->getLevel(), 0, 0, 0, 0, false, 0);
return Item::GetDisenchantLoot(itemTemplate, bonusData.Quality, itemLevel);
}
diff --git a/src/server/game/Handlers/AzeriteHandler.cpp b/src/server/game/Handlers/AzeriteHandler.cpp
new file mode 100644
index 00000000000..77ee2549c67
--- /dev/null
+++ b/src/server/game/Handlers/AzeriteHandler.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 "WorldSession.h"
+#include "AzeriteItem.h"
+#include "AzeritePackets.h"
+#include "DB2Stores.h"
+#include "Player.h"
+#include "SpellHistory.h"
+
+void WorldSession::HandleAzeriteEssenceUnlockMilestone(WorldPackets::Azerite::AzeriteEssenceUnlockMilestone& azeriteEssenceUnlockMilestone)
+{
+ if (!AzeriteItem::FindHeartForge(_player))
+ return;
+
+ Item* item = _player->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH);
+ if (!item)
+ return;
+
+ AzeriteItem* azeriteItem = item->ToAzeriteItem();
+ if (!azeriteItem || !azeriteItem->CanUseEssences())
+ return;
+
+ AzeriteItemMilestonePowerEntry const* milestonePower = sAzeriteItemMilestonePowerStore.LookupEntry(azeriteEssenceUnlockMilestone.AzeriteItemMilestonePowerID);
+ if (!milestonePower || milestonePower->RequiredLevel > int32(azeriteItem->GetLevel()))
+ return;
+
+ // check that all previous milestones are unlocked
+ for (AzeriteItemMilestonePowerEntry const* previousMilestone : sDB2Manager.GetAzeriteItemMilestonePowers())
+ {
+ if (previousMilestone == milestonePower)
+ break;
+
+ if (!azeriteItem->HasUnlockedEssenceMilestone(previousMilestone->ID))
+ return;
+ }
+
+ azeriteItem->AddUnlockedEssenceMilestone(milestonePower->ID);
+ _player->ApplyAzeriteItemMilestonePower(azeriteItem, milestonePower, true);
+ azeriteItem->SetState(ITEM_CHANGED, _player);
+}
+
+void WorldSession::HandleAzeriteEssenceActivateEssence(WorldPackets::Azerite::AzeriteEssenceActivateEssence& azeriteEssenceActivateEssence)
+{
+ WorldPackets::Azerite::AzeriteEssenceSelectionResult activateEssenceResult;
+ activateEssenceResult.AzeriteEssenceID = azeriteEssenceActivateEssence.AzeriteEssenceID;
+ Item* item = _player->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH);
+ if (!item || !item->IsEquipped())
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::NotEquipped;
+ activateEssenceResult.Slot = azeriteEssenceActivateEssence.Slot;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ AzeriteItem* azeriteItem = item->ToAzeriteItem();
+ if (!azeriteItem || !azeriteItem->CanUseEssences())
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::ConditionFailed;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ if (azeriteEssenceActivateEssence.Slot >= MAX_AZERITE_ESSENCE_SLOT || !azeriteItem->HasUnlockedEssenceSlot(azeriteEssenceActivateEssence.Slot))
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::SlotLocked;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ UF::SelectedAzeriteEssences const* selectedEssences = azeriteItem->GetSelectedAzeriteEssences();
+ // essence is already in that slot, nothing to do
+ if (selectedEssences->AzeriteEssenceID[azeriteEssenceActivateEssence.Slot] == uint32(azeriteEssenceActivateEssence.AzeriteEssenceID))
+ return;
+
+ uint32 rank = azeriteItem->GetEssenceRank(azeriteEssenceActivateEssence.AzeriteEssenceID);
+ if (!rank)
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::EssenceNotUnlocked;
+ activateEssenceResult.Arg = azeriteEssenceActivateEssence.AzeriteEssenceID;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ if (_player->IsInCombat())
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::AffectingCombat;
+ activateEssenceResult.Slot = azeriteEssenceActivateEssence.Slot;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ if (_player->isDead())
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::CantDoThatRightNow;
+ activateEssenceResult.Slot = azeriteEssenceActivateEssence.Slot;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ if (!_player->HasPlayerFlag(PLAYER_FLAGS_RESTING) && !_player->HasUnitFlag2(UNIT_FLAG2_ALLOW_CHANGING_TALENTS))
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::NotInRestArea;
+ activateEssenceResult.Slot = azeriteEssenceActivateEssence.Slot;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+
+ // need to remove selected essence from another slot if selected
+ int32 removeEssenceFromSlot = -1;
+ for (int32 slot = 0; slot < MAX_AZERITE_ESSENCE_SLOT; ++slot)
+ if (azeriteEssenceActivateEssence.Slot != uint8(slot) && selectedEssences->AzeriteEssenceID[slot] == uint32(azeriteEssenceActivateEssence.AzeriteEssenceID))
+ removeEssenceFromSlot = slot;
+
+ // check cooldown of major essence slot
+ if (selectedEssences->AzeriteEssenceID[0] && (azeriteEssenceActivateEssence.Slot == 0 || removeEssenceFromSlot == 0))
+ {
+ for (uint32 essenceRank = 1; essenceRank <= rank; ++essenceRank)
+ {
+ AzeriteEssencePowerEntry const* azeriteEssencePower = ASSERT_NOTNULL(sDB2Manager.GetAzeriteEssencePower(selectedEssences->AzeriteEssenceID[0], essenceRank));
+ if (_player->GetSpellHistory()->HasCooldown(azeriteEssencePower->MajorPowerDescription))
+ {
+ activateEssenceResult.Reason = AzeriteEssenceActivateResult::CantRemoveEssence;
+ activateEssenceResult.Arg = azeriteEssencePower->MajorPowerDescription;
+ activateEssenceResult.Slot = azeriteEssenceActivateEssence.Slot;
+ SendPacket(activateEssenceResult.Write());
+ return;
+ }
+ }
+ }
+
+ if (removeEssenceFromSlot != -1)
+ {
+ _player->ApplyAzeriteEssence(azeriteItem, selectedEssences->AzeriteEssenceID[removeEssenceFromSlot], MAX_AZERITE_ESSENCE_RANK,
+ AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(removeEssenceFromSlot)->Type) == AzeriteItemMilestoneType::MajorEssence, false);
+ azeriteItem->SetSelectedAzeriteEssence(removeEssenceFromSlot, 0);
+ }
+
+ if (selectedEssences->AzeriteEssenceID[azeriteEssenceActivateEssence.Slot])
+ {
+ _player->ApplyAzeriteEssence(azeriteItem, selectedEssences->AzeriteEssenceID[azeriteEssenceActivateEssence.Slot], MAX_AZERITE_ESSENCE_RANK,
+ AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(azeriteEssenceActivateEssence.Slot)->Type) == AzeriteItemMilestoneType::MajorEssence, false);
+ }
+
+ _player->ApplyAzeriteEssence(azeriteItem, azeriteEssenceActivateEssence.AzeriteEssenceID, rank,
+ AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(azeriteEssenceActivateEssence.Slot)->Type) == AzeriteItemMilestoneType::MajorEssence, true);
+
+ azeriteItem->SetSelectedAzeriteEssence(azeriteEssenceActivateEssence.Slot, azeriteEssenceActivateEssence.AzeriteEssenceID);
+ azeriteItem->SetState(ITEM_CHANGED, _player);
+}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 9dab59597e3..b4d71da14bc 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -156,6 +156,14 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AZERITE, stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER);
+ stmt->setUInt64(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AZERITE_MILESTONE_POWERS, stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE);
+ stmt->setUInt64(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AZERITE_UNLOCKED_ESSENCES, 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/Handlers/InspectHandler.cpp b/src/server/game/Handlers/InspectHandler.cpp
index 7f0117ae775..b05d4264f65 100644
--- a/src/server/game/Handlers/InspectHandler.cpp
+++ b/src/server/game/Handlers/InspectHandler.cpp
@@ -17,6 +17,7 @@
*/
#include "WorldSession.h"
+#include "AzeriteItem.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "InspectPackets.h"
@@ -65,6 +66,10 @@ void WorldSession::HandleInspectOpcode(WorldPackets::Inspect::Inspect& inspect)
inspectResult.GuildData->AchievementPoints = guild->GetAchievementMgr().GetAchievementPoints();
}
+ if (Item const* heartOfAzeroth = player->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH))
+ if (AzeriteItem const* azeriteItem = heartOfAzeroth->ToAzeriteItem())
+ inspectResult.AzeriteLevel = azeriteItem->GetEffectiveLevel();
+
inspectResult.ItemLevel = int32(player->GetAverageItemLevel());
inspectResult.LifetimeMaxRank = player->m_activePlayerData->LifetimeMaxRank;
inspectResult.TodayHK = player->m_activePlayerData->TodayHonorableKills;
diff --git a/src/server/game/Server/Packets/AzeritePackets.cpp b/src/server/game/Server/Packets/AzeritePackets.cpp
index 97e8bcc81ff..9f300d2d72d 100644
--- a/src/server/game/Server/Packets/AzeritePackets.cpp
+++ b/src/server/game/Server/Packets/AzeritePackets.cpp
@@ -16,6 +16,7 @@
*/
#include "AzeritePackets.h"
+#include "Util.h"
WorldPacket const* WorldPackets::Azerite::AzeriteXpGain::Write()
{
@@ -24,3 +25,33 @@ WorldPacket const* WorldPackets::Azerite::AzeriteXpGain::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Azerite::AzeriteEssenceForgeOpened::Write()
+{
+ _worldPacket << ForgeGUID;
+
+ return &_worldPacket;
+}
+
+void WorldPackets::Azerite::AzeriteEssenceUnlockMilestone::Read()
+{
+ _worldPacket >> AzeriteItemMilestonePowerID;
+}
+
+void WorldPackets::Azerite::AzeriteEssenceActivateEssence::Read()
+{
+ _worldPacket >> AzeriteEssenceID;
+ _worldPacket >> Slot;
+}
+
+WorldPacket const* WorldPackets::Azerite::AzeriteEssenceSelectionResult::Write()
+{
+ _worldPacket.WriteBits(AsUnderlyingType(Reason), 4);
+ _worldPacket.WriteBit(Slot.is_initialized());
+ _worldPacket << int32(Arg);
+ _worldPacket << int32(AzeriteEssenceID);
+ if (Slot)
+ _worldPacket << uint8(*Slot);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/AzeritePackets.h b/src/server/game/Server/Packets/AzeritePackets.h
index 31ca4cdb13a..fea3a799db6 100644
--- a/src/server/game/Server/Packets/AzeritePackets.h
+++ b/src/server/game/Server/Packets/AzeritePackets.h
@@ -19,7 +19,9 @@
#define AzeriteItemPackets_h__
#include "Packet.h"
+#include "ItemDefines.h"
#include "ObjectGuid.h"
+#include "Optional.h"
namespace WorldPackets
{
@@ -35,6 +37,58 @@ namespace WorldPackets
ObjectGuid ItemGUID;
uint64 XP = 0;
};
+
+ class AzeriteEssenceForgeOpened final : public ServerPacket
+ {
+ public:
+ AzeriteEssenceForgeOpened() : ServerPacket(SMSG_AZERITE_ESSENCE_FORGE_OPENED, 16) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid ForgeGUID;
+ };
+
+ class AzeriteEssenceForgeClose final : public ServerPacket
+ {
+ public:
+ AzeriteEssenceForgeClose() : ServerPacket(SMSG_AZERITE_ESSENCE_FORGE_CLOSE, 0) { }
+
+ WorldPacket const* Write() override { return &_worldPacket; }
+ };
+
+ class AzeriteEssenceUnlockMilestone final : public ClientPacket
+ {
+ public:
+ AzeriteEssenceUnlockMilestone(WorldPacket&& packet) : ClientPacket(CMSG_AZERITE_ESSENCE_UNLOCK_MILESTONE, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 AzeriteItemMilestonePowerID = 0;
+ };
+
+ class AzeriteEssenceActivateEssence final : public ClientPacket
+ {
+ public:
+ AzeriteEssenceActivateEssence(WorldPacket&& packet) : ClientPacket(CMSG_AZERITE_ESSENCE_ACTIVATE_ESSENCE, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 AzeriteEssenceID = 0;
+ uint8 Slot = 0;
+ };
+
+ class AzeriteEssenceSelectionResult final : public ServerPacket
+ {
+ public:
+ AzeriteEssenceSelectionResult() : ServerPacket(SMSG_AZERITE_ESSENCE_SELECTION_RESULT, 0) { }
+
+ WorldPacket const* Write() override;
+
+ AzeriteEssenceActivateResult Reason = AzeriteEssenceActivateResult::None;
+ int32 Arg = 0;
+ int32 AzeriteEssenceID = 0;
+ Optional<uint8> Slot;
+ };
}
}
diff --git a/src/server/game/Server/Packets/InspectPackets.cpp b/src/server/game/Server/Packets/InspectPackets.cpp
index 9442a6e0b73..cd5cc89db01 100644
--- a/src/server/game/Server/Packets/InspectPackets.cpp
+++ b/src/server/game/Server/Packets/InspectPackets.cpp
@@ -16,6 +16,7 @@
*/
#include "InspectPackets.h"
+#include "AzeriteItem.h"
#include "Item.h"
#include "Player.h"
@@ -166,6 +167,28 @@ WorldPackets::Inspect::InspectItemData::InspectItemData(::Item const* item, uint
}
++i;
}
+
+ if (AzeriteItem const* azeriteItem = item->ToAzeriteItem())
+ {
+ if (UF::SelectedAzeriteEssences const* essences = azeriteItem->GetSelectedAzeriteEssences())
+ {
+ for (uint8 slot = 0; slot < essences->AzeriteEssenceID.size(); ++slot)
+ {
+ AzeriteEssences.emplace_back();
+
+ WorldPackets::Inspect::AzeriteEssenceData& essence = AzeriteEssences.back();
+ essence.Index = slot;
+ essence.AzeriteEssenceID = essences->AzeriteEssenceID[slot];
+ if (essence.AzeriteEssenceID)
+ {
+ essence.Rank = azeriteItem->GetEssenceRank(essence.AzeriteEssenceID);
+ essence.SlotUnlocked = true;
+ }
+ else
+ essence.SlotUnlocked = azeriteItem->HasUnlockedEssenceSlot(slot);
+ }
+ }
+ }
}
WorldPacket const* WorldPackets::Inspect::InspectResult::Write()
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index b26656010ca..d7a6c6f7f94 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -184,8 +184,8 @@ void OpcodeTable::Initialize()
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_ESSENCE_ACTIVATE_ESSENCE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_AZERITE_ESSENCE_UNLOCK_MILESTONE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
+ 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);
DEFINE_HANDLER(CMSG_BATTLEFIELD_LEAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldLeaveOpcode);
DEFINE_HANDLER(CMSG_BATTLEFIELD_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldListOpcode);
@@ -949,9 +949,9 @@ void OpcodeTable::Initialize()
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_ESSENCE_FORGE_CLOSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_ESSENCE_FORGE_OPENED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_ESSENCE_SELECTION_RESULT, STATUS_UNHANDLED, 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);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AZERITE_XP_GAIN, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BAN_REASON, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BARBER_SHOP_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 51160bddb3f..808359495b6 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -109,6 +109,12 @@ namespace WorldPackets
enum class ConnectToSerial : uint32;
}
+ namespace Azerite
+ {
+ class AzeriteEssenceUnlockMilestone;
+ class AzeriteEssenceActivateEssence;
+ }
+
namespace Bank
{
class AutoBankItem;
@@ -1718,6 +1724,10 @@ class TC_GAME_API WorldSession
// Scenario
void HandleQueryScenarioPOI(WorldPackets::Scenario::QueryScenarioPOI& queryScenarioPOI);
+ // Azerite
+ void HandleAzeriteEssenceUnlockMilestone(WorldPackets::Azerite::AzeriteEssenceUnlockMilestone& azeriteEssenceUnlockMilestone);
+ void HandleAzeriteEssenceActivateEssence(WorldPackets::Azerite::AzeriteEssenceActivateEssence& azeriteEssenceActivateEssence);
+
union ConnectToKey
{
struct
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index d79594b22cc..9bb7a1eb444 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -117,6 +117,8 @@ void AuraApplication::_InitFlags(Unit* caster, uint32 effMask)
}
if (GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT) ||
+ GetBase()->HasEffectType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS) ||
+ GetBase()->HasEffectType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_TRIGGERED) ||
GetBase()->HasEffectType(SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN) ||
GetBase()->HasEffectType(SPELL_AURA_MOD_MAX_CHARGES) ||
GetBase()->HasEffectType(SPELL_AURA_CHARGE_RECOVERY_MOD) ||
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 24b2279c6e4..5e142928e28 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 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 cb82f7c3221..4d372838890 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 "AzeriteItem.h"
#include "Battleground.h"
#include "BattlegroundMgr.h"
#include "BattlePetMgr.h"
@@ -342,7 +343,7 @@ NonDefaultConstructible<pEffect> SpellEffects[TOTAL_SPELL_EFFECTS] =
&Spell::EffectNULL, //262 SPELL_EFFECT_262
&Spell::EffectNULL, //263 SPELL_EFFECT_REPAIR_ITEM
&Spell::EffectNULL, //264 SPELL_EFFECT_REMOVE_GEM
- &Spell::EffectNULL, //265 SPELL_EFFECT_LEARN_AZERITE_ESSENCE_POWER
+ &Spell::EffectLearnAzeriteEssencePower, //265 SPELL_EFFECT_LEARN_AZERITE_ESSENCE_POWER
&Spell::EffectNULL, //266 SPELL_EFFECT_266
&Spell::EffectNULL, //267 SPELL_EFFECT_267
&Spell::EffectNULL, //268 SPELL_EFFECT_APPLY_MOUNT_EQUIPMENT
@@ -5817,3 +5818,42 @@ void Spell::EffectLearnTransmogSet(SpellEffIndex /*effIndex*/)
unitTarget->ToPlayer()->GetSession()->GetCollectionMgr()->AddTransmogSet(effectInfo->MiscValue);
}
+
+void Spell::EffectLearnAzeriteEssencePower(SpellEffIndex /*effIndex*/)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ Player* playerTarget = unitTarget ? unitTarget->ToPlayer() : nullptr;
+ if (!playerTarget)
+ return;
+
+ Item* heartOfAzeroth = playerTarget->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH);
+ if (!heartOfAzeroth)
+ return;
+
+ AzeriteItem* azeriteItem = heartOfAzeroth->ToAzeriteItem();
+ if (!azeriteItem)
+ return;
+
+ // remove old rank and apply new one
+ if (azeriteItem->IsEquipped())
+ {
+ if (UF::SelectedAzeriteEssences const* selectedEssences = azeriteItem->GetSelectedAzeriteEssences())
+ {
+ for (int32 slot = 0; slot < MAX_AZERITE_ESSENCE_SLOT; ++slot)
+ {
+ if (selectedEssences->AzeriteEssenceID[slot] == uint32(effectInfo->MiscValue))
+ {
+ bool major = AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(slot)->Type) == AzeriteItemMilestoneType::MajorEssence;
+ playerTarget->ApplyAzeriteEssence(azeriteItem, effectInfo->MiscValue, MAX_AZERITE_ESSENCE_RANK, major, false);
+ playerTarget->ApplyAzeriteEssence(azeriteItem, effectInfo->MiscValue, effectInfo->MiscValueB, major, false);
+ break;
+ }
+ }
+ }
+ }
+
+ azeriteItem->SetEssenceRank(effectInfo->MiscValue, effectInfo->MiscValueB);
+ azeriteItem->SetState(ITEM_CHANGED, playerTarget);
+}