diff options
Diffstat (limited to 'src')
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); +} |