diff options
author | Shauren <shauren.trinity@gmail.com> | 2016-07-05 22:07:35 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-07-05 22:07:35 +0200 |
commit | f7883bd5251a759da1ca8be3ba6f6cead36723ec (patch) | |
tree | affce8f9ad4b343ba936ad6effa2127ab7d831b1 /src | |
parent | bc81ae70bc350a3decead610f1b17452bd44eec4 (diff) |
Core/Transmog: Implemented transmog collection and updated transmog handling
Diffstat (limited to 'src')
35 files changed, 1250 insertions, 376 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index ea5cc8ae3ae..fc729e04e5b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -22,7 +22,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() if (!m_reconnecting) m_stmts.resize(MAX_CHARACTERDATABASE_STATEMENTS); -#define SelectItemInstanceContent "ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId, ii.enchantIllusion, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.bonusListIDs, ig.gemItemId1, ig.gemItemId2, ig.gemItemId3" +#define SelectItemInstanceContent "ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, " \ + "ii.durability, ii.playedTime, ii.text, ii.upgradeId, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.bonusListIDs, " \ + "iit.itemModifiedAppearanceAllSpecs, iit.itemModifiedAppearanceSpec1, iit.itemModifiedAppearanceSpec2, iit.itemModifiedAppearanceSpec3, iit.itemModifiedAppearanceSpec4, " \ + "iit.spellItemEnchantmentAllSpecs, iit.spellItemEnchantmentSpec1, iit.spellItemEnchantmentSpec2, iit.spellItemEnchantmentSpec3, iit.spellItemEnchantmentSpec4, " \ + "ig.gemItemId1, ig.gemItemId2, ig.gemItemId3" PrepareStatement(CHAR_DEL_QUEST_POOL_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_QUEST_POOL_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC); @@ -108,7 +112,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT " SelectItemInstanceContent ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT " SelectItemInstanceContent ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC); @@ -138,8 +142,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT " SelectItemInstanceContent ", ii.owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid WHERE mail_id = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT " SelectItemInstanceContent " FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT " SelectItemInstanceContent ", ii.owner_guid FROM mail_items mi 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 WHERE mail_id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT " SelectItemInstanceContent " FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid", 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); PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC); @@ -161,14 +165,18 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_ITEM_BOP_TRADE, "DELETE FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ITEM_BOP_TRADE, "INSERT INTO item_soulbound_trade_data VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_INVENTORY_ITEM, "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ?, transmogrification = ?, upgradeId = ?, enchantIllusion = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, bonusListIDs = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, upgradeId, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ?, upgradeId = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, bonusListIDs = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ITEM_INSTANCE_GEMS, "INSERT INTO item_instance_gems (itemGuid, gemItemId1, gemItemId2, gemItemId3) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_GEMS, "DELETE FROM item_instance_gems WHERE itemGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_GEMS_BY_OWNER, "DELETE iig FROM item_instance_gems iig LEFT JOIN item_instance ii ON iig.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ITEM_INSTANCE_TRANSMOG, "INSERT INTO item_instance_transmog (itemGuid, itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, " + "spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG, "DELETE FROM item_instance_transmog WHERE itemGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG_BY_OWNER, "DELETE iit FROM item_instance_transmog iit LEFT JOIN item_instance ii ON iit.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_SYNCH); @@ -200,7 +208,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() // 0: uint32, 1: uint8, 2: uint8, 3: uint32, 4: uint32 PrepareStatement(CHAR_INS_GUILD_BANK_ITEM, "INSERT INTO guild_bank_item (guildid, TabId, SlotId, item_guid) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GUILD_BANK_ITEM, "DELETE FROM guild_bank_item WHERE guildid = ? AND TabId = ? AND SlotId = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8, 2: uint8 - PrepareStatement(CHAR_SEL_GUILD_BANK_ITEMS, "SELECT " SelectItemInstanceContent ", guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_GUILD_BANK_ITEMS, "SELECT " SelectItemInstanceContent ", guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.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", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_GUILD_BANK_ITEMS, "DELETE FROM guild_bank_item WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32 // 0: uint32, 1: uint8, 2: uint8, 3: uint8, 4: uint32 PrepareStatement(CHAR_INS_GUILD_BANK_RIGHT, "INSERT INTO guild_bank_right (guildid, TabId, rid, gbright, SlotPerDay) VALUES (?, ?, ?, ?, ?) " diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index fedc4087704..e58debb7939 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -147,6 +147,9 @@ enum CharacterDatabaseStatements CHAR_INS_ITEM_INSTANCE_GEMS, CHAR_DEL_ITEM_INSTANCE_GEMS, CHAR_DEL_ITEM_INSTANCE_GEMS_BY_OWNER, + CHAR_INS_ITEM_INSTANCE_TRANSMOG, + CHAR_DEL_ITEM_INSTANCE_TRANSMOG, + CHAR_DEL_ITEM_INSTANCE_TRANSMOG_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 e6b4f4480eb..210516eaf7c 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -441,6 +441,12 @@ void HotfixDatabaseConnection::DoPrepareStatements() "AllocationPct1, AllocationPct2, AllocationPct3, AllocationPct4, AllocationPct5 FROM item_random_suffix ORDER BY ID DESC", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_RANDOM_SUFFIX, "SELECT ID, Name_lang FROM item_random_suffix_locale WHERE locale = ?", CONNECTION_SYNCH); + // ItemSearchName.db2 + PrepareStatement(HOTFIX_SEL_ITEM_SEARCH_NAME, "SELECT ID, Name, Flags1, Flags2, Flags3, AllowableRace, RequiredSpell, RequiredReputationFaction, " + "RequiredSkill, RequiredSkillRank, ItemLevel, Quality, RequiredExpansion, RequiredReputationRank, RequiredLevel, AllowableClass" + " FROM item_search_name ORDER BY ID DESC", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_SEARCH_NAME, "SELECT ID, Name_lang FROM item_search_name_locale WHERE locale = ?", CONNECTION_SYNCH); + // ItemSet.db2 PrepareStatement(HOTFIX_SEL_ITEM_SET, "SELECT ID, Name, ItemID1, ItemID2, ItemID3, ItemID4, ItemID5, ItemID6, ItemID7, ItemID8, ItemID9, " "ItemID10, ItemID11, ItemID12, ItemID13, ItemID14, ItemID15, ItemID16, ItemID17, RequiredSkillRank, RequiredSkill, Flags FROM item_set" diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 5c3b0ddb196..f49e28b8de2 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -249,6 +249,9 @@ enum HotfixDatabaseStatements HOTFIX_SEL_ITEM_RANDOM_SUFFIX, HOTFIX_SEL_ITEM_RANDOM_SUFFIX_LOCALE, + HOTFIX_SEL_ITEM_SEARCH_NAME, + HOTFIX_SEL_ITEM_SEARCH_NAME_LOCALE, + HOTFIX_SEL_ITEM_SET, HOTFIX_SEL_ITEM_SET_LOCALE, diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 249839c079b..a11f5d2c677 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -156,4 +156,12 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_HEIRLOOMS, "SELECT itemId, flags FROM battlenet_account_heirlooms WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_REP_ACCOUNT_HEIRLOOMS, "REPLACE INTO battlenet_account_heirlooms (accountId, itemId, flags) VALUES (?, ?, ?)", CONNECTION_ASYNC); + + // Transmog collection + PrepareStatement(LOGIN_SEL_BNET_ITEM_APPEARANCES, "SELECT blobIndex, appearanceMask FROM battlenet_item_appearances WHERE battlenetAccountId = ? ORDER BY blobIndex DESC", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_BNET_ITEM_APPEARANCES, "INSERT INTO battlenet_item_appearances (battlenetAccountId, blobIndex, appearanceMask) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE appearanceMask = appearanceMask | VALUES(appearanceMask)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_BNET_ITEM_FAVORITE_APPEARANCES, "SELECT itemModifiedAppearanceId FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE, "INSERT INTO battlenet_item_favorite_appearances (battlenetAccountId, itemModifiedAppearanceId) VALUES (?, ?)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE, "DELETE FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ? AND itemModifiedAppearanceId = ?", CONNECTION_ASYNC); } diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 80fd8bc4dd8..29c16d3c974 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -146,6 +146,12 @@ enum LoginDatabaseStatements LOGIN_SEL_ACCOUNT_HEIRLOOMS, LOGIN_REP_ACCOUNT_HEIRLOOMS, + LOGIN_SEL_BNET_ITEM_APPEARANCES, + LOGIN_INS_BNET_ITEM_APPEARANCES, + LOGIN_SEL_BNET_ITEM_FAVORITE_APPEARANCES, + LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE, + LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE, + MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index f4a8d6423b8..2acf3205ddc 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -116,6 +116,7 @@ DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore("It DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore("ItemPriceBase.db2", ItemPriceBaseMeta::Instance(), HOTFIX_SEL_ITEM_PRICE_BASE); DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore("ItemRandomProperties.db2", ItemRandomPropertiesMeta::Instance(), HOTFIX_SEL_ITEM_RANDOM_PROPERTIES); DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore("ItemRandomSuffix.db2", ItemRandomSuffixMeta::Instance(), HOTFIX_SEL_ITEM_RANDOM_SUFFIX); +DB2Storage<ItemSearchNameEntry> sItemSearchNameStore("ItemSearchName.db2", ItemSearchNameMeta::Instance(), HOTFIX_SEL_ITEM_SEARCH_NAME); DB2Storage<ItemSetEntry> sItemSetStore("ItemSet.db2", ItemSetMeta::Instance(), HOTFIX_SEL_ITEM_SET); DB2Storage<ItemSetSpellEntry> sItemSetSpellStore("ItemSetSpell.db2", ItemSetSpellMeta::Instance(), HOTFIX_SEL_ITEM_SET_SPELL); DB2SparseStorage<ItemSparseEntry> sItemSparseStore("Item-sparse.db2", ItemSparseMeta::Instance(), HOTFIX_SEL_ITEM_SPARSE); @@ -374,6 +375,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sItemPriceBaseStore); LOAD_DB2(sItemRandomPropertiesStore); LOAD_DB2(sItemRandomSuffixStore); + LOAD_DB2(sItemSearchNameStore); LOAD_DB2(sItemSetStore); LOAD_DB2(sItemSetSpellStore); LOAD_DB2(sItemSparseStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index b5a052428f4..fc8407eb96b 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -106,6 +106,7 @@ TC_GAME_API extern DB2Storage<ItemModifiedAppearanceEntry> sItemModifie TC_GAME_API extern DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore; TC_GAME_API extern DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore; TC_GAME_API extern DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore; +TC_GAME_API extern DB2Storage<ItemSearchNameEntry> sItemSearchNameStore; TC_GAME_API extern DB2Storage<ItemSetEntry> sItemSetStore; TC_GAME_API extern DB2Storage<ItemSetSpellEntry> sItemSetSpellStore; TC_GAME_API extern DB2SparseStorage<ItemSparseEntry> sItemSparseStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index d5ff75ce421..47e54d1f7af 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1317,6 +1317,24 @@ struct ItemRandomSuffixEntry uint16 AllocationPct[MAX_ITEM_RANDOM_PROPERTIES]; }; +struct ItemSearchNameEntry +{ + uint32 ID; + LocalizedString* Name; + uint32 Flags[3]; + uint32 AllowableRace; + uint32 RequiredSpell; + uint16 RequiredReputationFaction; + uint16 RequiredSkill; + uint16 RequiredSkillRank; + uint16 ItemLevel; + uint8 Quality; + uint8 RequiredExpansion; + uint8 RequiredReputationRank; + uint8 RequiredLevel; + uint32 AllowableClass; +}; + #define MAX_ITEM_SET_ITEMS 17 struct ItemSetEntry @@ -1717,7 +1735,7 @@ struct PhaseXPhaseGroupEntry struct PlayerConditionEntry { - uint32 ID; // 1, 1, 1 + uint32 ID; uint32 RaceMask; uint32 SkillLogic; uint32 ReputationLogic; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 9fbea011dd6..9bb17ec1fa5 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -32,6 +32,7 @@ #include "ItemPackets.h" #include "TradeData.h" #include "GameTables.h" +#include "CollectionMgr.h" void AddItemsSetItem(Player* player, Item* item) { @@ -234,6 +235,34 @@ bool ItemCanGoIntoBag(ItemTemplate const* pProto, ItemTemplate const* pBagProto) return false; } +ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS] = +{ + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4 +}; + +static uint32 const AppearanceModifierMaskSpecSpecific = + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4); + +ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS] = +{ + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4 +}; + +static uint32 const IllusionModifierMaskSpecSpecific = + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4); + Item::Item() { m_objectType |= TYPEMASK_ITEM; @@ -356,9 +385,7 @@ void Item::SaveToDB(SQLTransaction& trans) stmt->setUInt16(++index, GetUInt32Value(ITEM_FIELD_DURABILITY)); stmt->setUInt32(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)); stmt->setString(++index, m_text); - stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID) | (GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD) << 24)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_UPGRADE_ID)); - stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL)); @@ -395,6 +422,43 @@ void Item::SaveToDB(SQLTransaction& trans) stmt->setUInt32(1 + i, 0); trans->Append(stmt); } + + static ItemModifier const transmogMods[10] = + { + ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, + + ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, + }; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG); + stmt->setUInt64(0, GetGUID().GetCounter()); + trans->Append(stmt); + + if (std::find_if(std::begin(transmogMods), std::end(transmogMods), [this](ItemModifier modifier) { return GetModifier(modifier) != 0; }) != std::end(transmogMods)) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_TRANSMOG); + stmt->setUInt64(0, GetGUID().GetCounter()); + stmt->setUInt32(1, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + stmt->setUInt32(2, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1)); + stmt->setUInt32(3, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2)); + stmt->setUInt32(4, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3)); + stmt->setUInt32(5, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4)); + stmt->setUInt32(6, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + stmt->setUInt32(7, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1)); + stmt->setUInt32(8, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2)); + stmt->setUInt32(9, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3)); + stmt->setUInt32(10, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4)); + trans->Append(stmt); + } + break; } case ITEM_REMOVED: @@ -436,12 +500,16 @@ void Item::SaveToDB(SQLTransaction& trans) bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fields, uint32 entry) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 - //result = CharacterDatabase.PQuery("SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, - // 13 14 15 16 17 18 19 20 - // transmogrification, upgradeId, enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, - // 21 22 23 - // gemItemId1, gemItemId2, gemItemId3 FROM item_instance WHERE guid = '%u'", guid); + // 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, + // 13 14 15 16 17 18 + // upgradeId, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, + // 19 20 21 22 23 + // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, + // 24 25 26 27 28 + // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, + // 29 30 31 + // gemItemId1, gemItemId2, gemItemId3 FROM item_instance // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB @@ -509,19 +577,32 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, fields[11].GetUInt32()); SetText(fields[12].GetString()); - if (uint32 transmogEntry = fields[13].GetUInt32()) + SetModifier(ITEM_MODIFIER_UPGRADE_ID, fields[13].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, fields[14].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, fields[15].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[16].GetUInt16()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[17].GetUInt32()); + + Tokenizer bonusListIDs(fields[18].GetString(), ' '); + for (char const* token : bonusListIDs) { - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, (transmogEntry >> 24) & 0xFF); - SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogEntry & 0xFFFFFF); + uint32 bonusListID = atoul(token); + AddBonuses(bonusListID); } - SetModifier(ITEM_MODIFIER_UPGRADE_ID, fields[14].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION, fields[15].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, fields[16].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, fields[17].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[18].GetUInt16()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[19].GetUInt32()); - - uint32 gemItemIds[3] = { fields[21].GetUInt32(), fields[22].GetUInt32(), fields[23].GetUInt32() }; + + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[19].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[20].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[21].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[22].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[23].GetUInt32()); + + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[24].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[25].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[26].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[27].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[28].GetUInt32()); + + uint32 gemItemIds[3] = { fields[29].GetUInt32(), fields[30].GetUInt32(), fields[31].GetUInt32() }; if (gemItemIds[0] || gemItemIds[1] || gemItemIds[2]) { // gem slots must be preserved, hence funky logic @@ -532,13 +613,6 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie AddDynamicValue(ITEM_DYNAMIC_FIELD_GEMS, gemItemIds[2]); } - Tokenizer bonusListIDs(fields[20].GetString(), ' '); - for (char const* token : bonusListIDs) - { - uint32 bonusListID = atoul(token); - AddBonuses(bonusListID); - } - if (need_save) // normal item changed state set not work at loading { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD); @@ -1130,7 +1204,7 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* arrayMask.SetCount(values.size()); for (std::size_t v = 0; v < values.size(); ++v) { - if (updateType != UPDATETYPE_VALUES || _dynamicChangesArrayMask[index].GetBit(v)) + if (updateType == UPDATETYPE_VALUES ? _dynamicChangesArrayMask[index].GetBit(v) : values[v]) { arrayMask.SetBit(v); buffer << uint32(values[v]); @@ -1149,7 +1223,6 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* buffer << uint32(modifier); } } - } fieldBuffer << uint16(arrayMask.GetBlockCount()); @@ -1204,7 +1277,7 @@ void Item::DeleteRefundDataFromDB(SQLTransaction* trans) } } -void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=NULL*/) +void Item::SetNotRefundable(Player* owner, bool changestate /*= true*/, SQLTransaction* trans /*= nullptr*/, bool addToCollection /*= true*/) { if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) return; @@ -1224,6 +1297,8 @@ void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransa DeleteRefundDataFromDB(trans); owner->DeleteRefundReference(GetGUID()); + if (addToCollection) + owner->GetSession()->GetCollectionMgr()->AddItemAppearance(this); } void Item::UpdatePlayedTime(Player* owner) @@ -1278,6 +1353,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner) if (allowedGUIDs.empty()) return; + currentOwner->GetSession()->GetCollectionMgr()->AddItemAppearance(this); allowedGUIDs.clear(); SetState(ITEM_CHANGED, currentOwner); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE); @@ -1303,9 +1379,6 @@ bool Item::IsValidTransmogrificationTarget() const if (!proto) return false; - if (proto->GetQuality() == ITEM_QUALITY_LEGENDARY) - return false; - if (proto->GetClass() != ITEM_CLASS_ARMOR && proto->GetClass() != ITEM_CLASS_WEAPON) return false; @@ -1322,34 +1395,6 @@ bool Item::IsValidTransmogrificationTarget() const return true; } -bool Item::IsValidTransmogrificationSource(WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus) -{ - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(transmogrifier.ItemID); - if (!proto) - return false; - - if (proto->GetFlags2() & ITEM_FLAG2_CANNOT_TRANSMOG) - return false; - - if (proto->GetQuality() == ITEM_QUALITY_LEGENDARY) - return false; - - if (proto->GetClass() != ITEM_CLASS_ARMOR && - proto->GetClass() != ITEM_CLASS_WEAPON) - return false; - - if (proto->GetClass() == ITEM_CLASS_WEAPON && proto->GetSubClass() == ITEM_SUBCLASS_WEAPON_FISHING_POLE) - return false; - - if (proto->GetFlags2() & ITEM_FLAG2_CAN_TRANSMOG) - return true; - - if (!HasStats(transmogrifier, bonus)) - return false; - - return true; -} - bool Item::HasStats() const { if (GetItemRandomPropertyId() != 0) @@ -1456,18 +1501,18 @@ int32 const ItemTransmogrificationSlots[MAX_INVTYPE] = -1 // INVTYPE_RELIC }; -bool Item::CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus) +bool Item::CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceEntry const* itemModifiedAppearance) { - ItemTemplate const* source = sObjectMgr->GetItemTemplate(transmogrifier.ItemID); // source - ItemTemplate const* target = transmogrified->GetTemplate(); // dest + ItemTemplate const* source = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID); // source + ItemTemplate const* target = item->GetTemplate(); // dest if (!source || !target) return false; - if (sDB2Manager.GetItemDisplayId(source->GetId(), bonus->AppearanceModID) == transmogrified->GetDisplayId()) + if (itemModifiedAppearance == item->GetItemModifiedAppearance()) return false; - if (!IsValidTransmogrificationSource(transmogrifier, bonus) || !transmogrified->IsValidTransmogrificationTarget()) + if (!item->IsValidTransmogrificationTarget()) return false; if (source->GetClass() != target->GetClass()) @@ -1483,11 +1528,19 @@ bool Item::CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets: if (source->GetSubClass() != target->GetSubClass()) { - if (source->GetClass() != ITEM_CLASS_WEAPON) - return false; - - if (GetTransmogrificationWeaponCategory(source) != GetTransmogrificationWeaponCategory(target)) - return false; + switch (source->GetClass()) + { + case ITEM_CLASS_WEAPON: + if (GetTransmogrificationWeaponCategory(source) != GetTransmogrificationWeaponCategory(target)) + return false; + break; + case ITEM_CLASS_ARMOR: + if (source->GetSubClass() != ITEM_SUBCLASS_ARMOR_COSMETIC) + return false; + break; + default: + return false; + } } if (source->GetInventoryType() != target->GetInventoryType()) @@ -1863,10 +1916,14 @@ int32 Item::GetItemStatValue(uint32 index, Player const* owner) const return _bonusData.ItemStatValue[index]; } -uint32 Item::GetDisplayId() const +uint32 Item::GetDisplayId(Player const* owner) const { - if (uint32 transmogrification = GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return sDB2Manager.GetItemDisplayId(transmogrification, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD)); + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return sDB2Manager.GetItemDisplayId(transmog->ItemID, transmog->AppearanceModID); return sDB2Manager.GetItemDisplayId(GetEntry(), GetAppearanceModId()); } @@ -1882,33 +1939,46 @@ void Item::SetModifier(ItemModifier modifier, uint32 value) ApplyModFlag(ITEM_FIELD_MODIFIERS_MASK, 1 << modifier, value != 0); } -uint32 Item::GetVisibleEntry() const +uint32 Item::GetVisibleEntry(Player const* owner) const { - if (uint32 transmogrification = GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return transmogrification; + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return transmog->ItemID; return GetEntry(); } -uint16 Item::GetVisibleAppearanceModId() const +uint16 Item::GetVisibleAppearanceModId(Player const* owner) const { - if (GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return uint16(GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD)); + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return transmog->AppearanceModID; return uint16(GetAppearanceModId()); } -uint32 Item::GetVisibleEnchantmentId() const +uint32 Item::GetVisibleEnchantmentId(Player const* owner) const { - if (uint32 enchantIllusion = GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION)) + ItemModifier illusionModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, IllusionModifierMaskSpecSpecific)) + illusionModifier = IllusionModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(illusionModifier))) + if (uint32 enchantIllusion = GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)) return enchantIllusion; return GetEnchantmentId(PERM_ENCHANTMENT_SLOT); } -uint16 Item::GetVisibleItemVisual() const +uint16 Item::GetVisibleItemVisual(Player const* owner) const { - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetVisibleEnchantmentId())) + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetVisibleEnchantmentId(owner))) return enchant->ItemVisual; return 0; diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 7f52e3874fe..12a33f86a23 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -208,10 +208,12 @@ enum EnchantmentOffset enum EnchantmentSlotMask { - ENCHANTMENT_CAN_SOULBOUND = 0x01, - ENCHANTMENT_UNK1 = 0x02, - ENCHANTMENT_UNK2 = 0x04, - ENCHANTMENT_UNK3 = 0x08 + ENCHANTMENT_CAN_SOULBOUND = 0x01, + ENCHANTMENT_UNK1 = 0x02, + ENCHANTMENT_UNK2 = 0x04, + ENCHANTMENT_UNK3 = 0x08, + ENCHANTMENT_COLLECTABLE = 0x100, + ENCHANTMENT_HIDE_IF_NOT_COLLECTED = 0x200, }; enum ItemUpdateState @@ -224,14 +226,29 @@ enum ItemUpdateState enum ItemModifier { - ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD = 0, - ITEM_MODIFIER_TRANSMOG_ITEM_ID = 1, - ITEM_MODIFIER_UPGRADE_ID = 2, - ITEM_MODIFIER_BATTLE_PET_SPECIES_ID = 3, - ITEM_MODIFIER_BATTLE_PET_BREED_DATA = 4, // (breedId) | (breedQuality << 24) - ITEM_MODIFIER_BATTLE_PET_LEVEL = 5, - ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID = 6, - ITEM_MODIFIER_ENCHANT_ILLUSION = 7, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS = 0, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1 = 1, + ITEM_MODIFIER_UPGRADE_ID = 2, + ITEM_MODIFIER_BATTLE_PET_SPECIES_ID = 3, + ITEM_MODIFIER_BATTLE_PET_BREED_DATA = 4, // (breedId) | (breedQuality << 24) + ITEM_MODIFIER_BATTLE_PET_LEVEL = 5, + ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID = 6, + ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS = 7, + ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID = 8, + ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL = 9, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1 = 10, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2 = 11, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2 = 12, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3 = 13, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3 = 13, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4 = 15, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4 = 16, + ITEM_MODIFIER_CHALLENGE_MAP_CHALLENGE_MODE_ID = 17, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_LEVEL = 18, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_1 = 19, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_2 = 20, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_3 = 21, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_IS_CHARGED = 22, MAX_ITEM_MODIFIERS }; @@ -239,6 +256,8 @@ enum ItemModifier #define MAX_ITEM_SPELLS 5 bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto); +extern ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS]; +extern ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS]; struct BonusData { @@ -397,13 +416,13 @@ class TC_GAME_API Item : public Object uint32 GetAppearanceModId() const { return _bonusData.AppearanceModID; } uint32 GetArmor(Player const* owner) const { return GetTemplate()->GetArmor(GetItemLevel(owner)); } void GetDamage(Player const* owner, float& minDamage, float& maxDamage) const { GetTemplate()->GetDamage(GetItemLevel(owner), minDamage, maxDamage); } - uint32 GetDisplayId() const; + uint32 GetDisplayId(Player const* owner) const; ItemModifiedAppearanceEntry const* GetItemModifiedAppearance() const; float GetRepairCostMultiplier() const { return _bonusData.RepairCostMultiplier; } uint32 GetScalingStatDistribution() const { return _bonusData.ScalingStatDistribution; } // Item Refund system - void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = NULL); + void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = nullptr, bool addToCollection = true); void SetRefundRecipient(ObjectGuid const& guid) { m_refundRecipient = guid; } void SetPaidMoney(uint32 money) { m_paidMoney = money; } void SetPaidExtendedCost(uint32 iece) { m_paidExtendedCost = iece; } @@ -429,17 +448,16 @@ class TC_GAME_API Item : public Object uint32 GetScriptId() const { return GetTemplate()->ScriptId; } bool IsValidTransmogrificationTarget() const; - static bool IsValidTransmogrificationSource(WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus); bool HasStats() const; static bool HasStats(WorldPackets::Item::ItemInstance const& itemInstance, BonusData const* bonus); - static bool CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus); + static bool CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceEntry const* itemModifiedAppearance); static uint32 GetSpecialPrice(ItemTemplate const* proto, uint32 minimumPrice = 10000); uint32 GetSpecialPrice(uint32 minimumPrice = 10000) const { return Item::GetSpecialPrice(GetTemplate(), minimumPrice); } - uint32 GetVisibleEntry() const; - uint16 GetVisibleAppearanceModId() const; - uint32 GetVisibleEnchantmentId() const; - uint16 GetVisibleItemVisual() const; + uint32 GetVisibleEntry(Player const* owner) const; + uint16 GetVisibleAppearanceModId(Player const* owner) const; + uint32 GetVisibleEnchantmentId(Player const* owner) const; + uint16 GetVisibleItemVisual(Player const* owner) const; static uint32 GetSellPrice(ItemTemplate const* proto, bool& success); diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 2627f9cf328..756d21e7856 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -217,7 +217,7 @@ enum ItemFlags2 : uint32 ITEM_FLAG2_NEED_ROLL_DISABLED = 0x00000100, ITEM_FLAG2_CASTER_WEAPON = 0x00000200, ITEM_FLAG2_UNK6 = 0x00000400, - ITEM_FLAG2_UNK7 = 0x00000800, + ITEM_FLAG2_UNAVAILABLE_FOR_PLAYERS= 0x00000800, ITEM_FLAG2_UNK8 = 0x00001000, ITEM_FLAG2_UNK9 = 0x00002000, ITEM_FLAG2_HAS_NORMAL_PRICE = 0x00004000, @@ -490,7 +490,7 @@ enum ItemSubclassArmor ITEM_SUBCLASS_ARMOR_LEATHER = 2, ITEM_SUBCLASS_ARMOR_MAIL = 3, ITEM_SUBCLASS_ARMOR_PLATE = 4, - ITEM_SUBCLASS_ARMOR_BUCKLER = 5, // OBSOLETE + ITEM_SUBCLASS_ARMOR_COSMETIC = 5, ITEM_SUBCLASS_ARMOR_SHIELD = 6, ITEM_SUBCLASS_ARMOR_LIBRAM = 7, ITEM_SUBCLASS_ARMOR_IDOL = 8, @@ -769,6 +769,7 @@ struct TC_GAME_API ItemTemplate uint32 FlagsCu; float SpellPPMRate; std::unordered_set<uint32> Specializations[2]; // one set for 1-40 level range and another for 41-100 + uint32 ItemSpecClassMask; // helpers bool CanChangeEquipStateInCombat() const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b1a915e474e..028f954744f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -813,7 +813,7 @@ void Object::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player arrayMask.SetCount(values.size()); for (std::size_t v = 0; v < values.size(); ++v) { - if (updateType != UPDATETYPE_VALUES || _dynamicChangesArrayMask[index].GetBit(v)) + if (updateType == UPDATETYPE_VALUES ? _dynamicChangesArrayMask[index].GetBit(v) : values[v]) { arrayMask.SetBit(v); buffer << uint32(values[v]); @@ -1382,10 +1382,23 @@ void Object::AddDynamicValue(uint16 index, uint32 value) AddToObjectUpdateIfNeeded(); } -void Object::RemoveDynamicValue(uint16 index, uint32 /*value*/) +void Object::RemoveDynamicValue(uint16 index, uint32 value) { ASSERT(index < _dynamicValuesCount || PrintIndexError(index, false)); - /// TODO: Research if this is actually needed + + // TODO: Research if this is blizzlike to just set value to 0 + std::vector<uint32>& values = _dynamicValues[index]; + for (std::size_t i = 0; i < values.size(); ++i) + { + if (values[i] == value) + { + values[i] = 0; + _dynamicChangesMask.SetBit(index); + _dynamicChangesArrayMask[index].SetBit(i); + + AddToObjectUpdateIfNeeded(); + } + } } void Object::ClearDynamicValue(uint16 index) diff --git a/src/server/game/Entities/Player/CollectionMgr.cpp b/src/server/game/Entities/Player/CollectionMgr.cpp index 2def446ccfb..0dffcc6e8ad 100644 --- a/src/server/game/Entities/Player/CollectionMgr.cpp +++ b/src/server/game/Entities/Player/CollectionMgr.cpp @@ -16,7 +16,13 @@ */ #include "CollectionMgr.h" +#include "ObjectMgr.h" #include "Player.h" +#include "TransmogrificationPackets.h" + +CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner), _appearances() +{ +} void CollectionMgr::LoadToys() { @@ -77,6 +83,14 @@ void CollectionMgr::ToySetFavorite(uint32 itemId, bool favorite) itr->second = favorite; } +void CollectionMgr::OnItemAdded(Item* item) +{ + if (sDB2Manager.GetHeirloomByItemId(item->GetEntry())) + AddHeirloom(item->GetEntry(), 0); + + AddItemAppearance(item); +} + void CollectionMgr::LoadAccountHeirlooms(PreparedQueryResult result) { if (!result) @@ -259,3 +273,353 @@ bool CollectionMgr::CanApplyHeirloomXpBonus(uint32 itemId, uint32 level) return level <= 60; } + +struct DynamicBitsetBlockOutputIterator : public std::iterator<std::output_iterator_tag, void, void, void, void> +{ + explicit DynamicBitsetBlockOutputIterator(std::function<void(uint32)>&& action) : _action(std::forward<std::function<void(uint32)>>(action)) { } + + DynamicBitsetBlockOutputIterator& operator=(uint32 value) + { + _action(value); + return *this; + } + + DynamicBitsetBlockOutputIterator& operator*() { return *this; } + DynamicBitsetBlockOutputIterator& operator++() { return *this; } + DynamicBitsetBlockOutputIterator operator++(int) { return *this; } + +private: + std::function<void(uint32)> _action; +}; + +void CollectionMgr::LoadItemAppearances() +{ + boost::to_block_range(_appearances, DynamicBitsetBlockOutputIterator([this](uint32 blockValue) + { + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockValue); + })); + + for (auto itr = _temporaryAppearances.begin(); itr != _temporaryAppearances.end(); ++itr) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itr->first); +} + +void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances) +{ + if (knownAppearances) + { + std::vector<uint32> blocks; + do + { + Field* fields = knownAppearances->Fetch(); + uint16 blobIndex = fields[0].GetUInt16(); + if (blobIndex >= blocks.size()) + blocks.resize(blobIndex + 1); + + blocks[blobIndex] = fields[1].GetUInt32(); + + } while (knownAppearances->NextRow()); + + _appearances.init_from_block_range(blocks.begin(), blocks.end()); + } + + if (favoriteAppearances) + { + do + { + _favoriteAppearances[favoriteAppearances->Fetch()[0].GetUInt32()] = FavoriteAppearanceState::Unchanged; + } while (favoriteAppearances->NextRow()); + } + + // Static item appearances known by every player + static uint32 const hiddenAppearanceItems[3] = { 134110, 134111, 134112 }; + for (uint32 hiddenItem : hiddenAppearanceItems) + { + ItemModifiedAppearanceEntry const* hiddenAppearance = sDB2Manager.GetItemModifiedAppearance(hiddenItem, 0); + ASSERT(hiddenAppearance); + if (_appearances.size() <= hiddenAppearance->ID) + _appearances.resize(hiddenAppearance->ID + 1); + + _appearances.set(hiddenAppearance->ID); + } +} + +void CollectionMgr::SaveAccountItemAppearances(SQLTransaction& trans) +{ + uint16 blockIndex = 0; + boost::to_block_range(_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue) + { + if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ITEM_APPEARANCES); + stmt->setUInt32(0, GetOwner()->GetBattlenetAccountId()); + stmt->setUInt16(1, blockIndex); + stmt->setUInt32(2, blockValue); + trans->Append(stmt); + } + + ++blockIndex; + })); + + PreparedStatement* stmt; + for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end();) + { + switch (itr->second) + { + case FavoriteAppearanceState::New: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + itr->second = FavoriteAppearanceState::Unchanged; + ++itr; + break; + case FavoriteAppearanceState::Removed: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + itr = _favoriteAppearances.erase(itr); + break; + case FavoriteAppearanceState::Unchanged: + ++itr; + break; + } + } +} + +uint32 const PlayerClassByArmorSubclass[MAX_ITEM_SUBCLASS_ARMOR] = +{ + CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_MISCELLANEOUS + (1 << (CLASS_PRIEST - 1)) | (1 << (CLASS_MAGE - 1)) | (1 << (CLASS_WARLOCK - 1)), //ITEM_SUBCLASS_ARMOR_CLOTH + (1 << (CLASS_ROGUE - 1)) | (1 << (CLASS_MONK - 1)) | (1 << (CLASS_DRUID - 1)) | (1 << (CLASS_DEMON_HUNTER - 1)), //ITEM_SUBCLASS_ARMOR_LEATHER + (1 << (CLASS_HUNTER - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_MAIL + (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)), //ITEM_SUBCLASS_ARMOR_PLATE + CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_BUCKLER + (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_SHIELD + 1 << (CLASS_PALADIN - 1), //ITEM_SUBCLASS_ARMOR_LIBRAM + 1 << (CLASS_DRUID - 1), //ITEM_SUBCLASS_ARMOR_IDOL + 1 << (CLASS_SHAMAN - 1), //ITEM_SUBCLASS_ARMOR_TOTEM + 1 << (CLASS_DEATH_KNIGHT - 1), //ITEM_SUBCLASS_ARMOR_SIGIL + (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)) | (1 << (CLASS_SHAMAN - 1)) | (1 << (CLASS_DRUID - 1)), //ITEM_SUBCLASS_ARMOR_RELIC +}; + + +void CollectionMgr::AddItemAppearance(Item* item) +{ + if (!item->IsSoulBound()) + return; + + ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance(); + if (!CanAddAppearance(itemModifiedAppearance)) + return; + + if (item->GetUInt32Value(ITEM_FIELD_FLAGS) & (ITEM_FIELD_FLAG_BOP_TRADEABLE | ITEM_FIELD_FLAG_REFUNDABLE)) + { + AddTemporaryAppearance(item->GetGUID(), itemModifiedAppearance); + return; + } + + AddItemAppearance(itemModifiedAppearance); +} + +void CollectionMgr::AddItemAppearance(uint32 itemId) +{ + ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(itemId, 0); + if (!CanAddAppearance(itemModifiedAppearance)) + return; + + AddItemAppearance(itemModifiedAppearance); +} + +bool CollectionMgr::CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const +{ + if (!itemModifiedAppearance) + return false; + + if (itemModifiedAppearance->SourceType == 6 || itemModifiedAppearance->SourceType == 9) + return false; + + if (!sItemSearchNameStore.LookupEntry(itemModifiedAppearance->ItemID)) + return false; + + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID); + if (!itemTemplate) + return false; + + if (_owner->GetPlayer()->CanUseItem(itemTemplate) != EQUIP_ERR_OK) + return false; + + if (itemTemplate->GetFlags2() & ITEM_FLAG2_CANNOT_TRANSMOG || itemTemplate->GetQuality() == ITEM_QUALITY_ARTIFACT) + return false; + + switch (itemTemplate->GetClass()) + { + case ITEM_CLASS_WEAPON: + { + if (!(_owner->GetPlayer()->GetWeaponProficiency() & (1 << itemTemplate->GetSubClass()))) + return false; + if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC2 || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_MISCELLANEOUS || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_THROWN || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_SPEAR || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_FISHING_POLE) + return false; + break; + } + case ITEM_CLASS_ARMOR: + { + switch (itemTemplate->GetInventoryType()) + { + case INVTYPE_BODY: + case INVTYPE_SHIELD: + case INVTYPE_CLOAK: + case INVTYPE_TABARD: + case INVTYPE_HOLDABLE: + break; + case INVTYPE_HEAD: + case INVTYPE_SHOULDERS: + case INVTYPE_CHEST: + case INVTYPE_WAIST: + case INVTYPE_LEGS: + case INVTYPE_FEET: + case INVTYPE_WRISTS: + case INVTYPE_HANDS: + case INVTYPE_ROBE: + if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_ARMOR_MISCELLANEOUS) + return false; + break; + default: + return false; + } + if (itemTemplate->GetInventoryType() != INVTYPE_CLOAK) + if (!(PlayerClassByArmorSubclass[itemTemplate->GetSubClass()] & _owner->GetPlayer()->getClassMask())) + return false; + break; + } + default: + return false; + } + + if (itemTemplate->GetQuality() < ITEM_QUALITY_UNCOMMON) + if (!(itemTemplate->GetFlags2() & ITEM_FLAG2_CAN_TRANSMOG) || !(itemTemplate->GetFlags3() & 0x200000)) + return false; + + if (itemModifiedAppearance->ID < _appearances.size() && _appearances.test(itemModifiedAppearance->ID)) + return false; + + return true; +} + +void CollectionMgr::AddItemAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) +{ + if (_appearances.size() <= itemModifiedAppearance->ID) + { + uint32 numBlocks = _appearances.num_blocks(); + _appearances.resize(itemModifiedAppearance->ID + 1); + numBlocks = _appearances.num_blocks() - numBlocks; + while (numBlocks--) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, 0); + } + + _appearances.set(itemModifiedAppearance->ID); + uint32 blockIndex = itemModifiedAppearance->ID / 32; + uint32 bitIndex = itemModifiedAppearance->ID % 32; + uint32 currentMask = _owner->GetPlayer()->GetDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockIndex); + _owner->GetPlayer()->SetDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockIndex, currentMask | (1 << bitIndex)); + auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearance->ID); + if (temporaryAppearance != _temporaryAppearances.end()) + { + _owner->GetPlayer()->RemoveDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + _temporaryAppearances.erase(temporaryAppearance); + } +} + +void CollectionMgr::AddTemporaryAppearance(ObjectGuid const& itemGuid, ItemModifiedAppearanceEntry const* itemModifiedAppearance) +{ + std::unordered_set<ObjectGuid>& itemsWithAppearance = _temporaryAppearances[itemModifiedAppearance->ID]; + if (itemsWithAppearance.empty()) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + + itemsWithAppearance.insert(itemGuid); +} + +void CollectionMgr::RemoveTemporaryAppearance(Item* item) +{ + ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance(); + if (!itemModifiedAppearance) + return; + + auto itr = _temporaryAppearances.find(itemModifiedAppearance->ID); + if (itr == _temporaryAppearances.end()) + return; + + itr->second.erase(item->GetGUID()); + if (itr->second.empty()) + { + _owner->GetPlayer()->RemoveDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + _temporaryAppearances.erase(itr); + } +} + +std::pair<bool, bool> CollectionMgr::HasItemAppearance(uint32 itemModifiedAppearanceId) const +{ + if (itemModifiedAppearanceId < _appearances.size() && _appearances.test(itemModifiedAppearanceId)) + return{ true, false }; + + if (_temporaryAppearances.find(itemModifiedAppearanceId) != _temporaryAppearances.end()) + return{ true,true }; + + return{ false,false }; +} + +std::unordered_set<ObjectGuid> CollectionMgr::GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const +{ + auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearanceId); + if (temporaryAppearance != _temporaryAppearances.end()) + return temporaryAppearance->second; + + return{}; +} + +void CollectionMgr::SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply) +{ + auto itr = _favoriteAppearances.find(itemModifiedAppearanceId); + if (apply) + { + if (itr == _favoriteAppearances.end()) + _favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState::New; + else if (itr->second == FavoriteAppearanceState::Removed) + itr->second = FavoriteAppearanceState::Unchanged; + else + return; + } + else if (itr != _favoriteAppearances.end()) + { + if (itr->second == FavoriteAppearanceState::New) + _favoriteAppearances.erase(itemModifiedAppearanceId); + else + itr->second = FavoriteAppearanceState::Removed; + } + else + return; + + WorldPackets::Transmogrification::TransmogCollectionUpdate transmogCollectionUpdate; + transmogCollectionUpdate.IsFullUpdate = false; + transmogCollectionUpdate.IsSetFavorite = apply; + transmogCollectionUpdate.FavoriteAppearances.push_back(itemModifiedAppearanceId); + + _owner->SendPacket(transmogCollectionUpdate.Write()); +} + +void CollectionMgr::SendFavoriteAppearances() const +{ + WorldPackets::Transmogrification::TransmogCollectionUpdate transmogCollectionUpdate; + transmogCollectionUpdate.IsFullUpdate = true; + transmogCollectionUpdate.FavoriteAppearances.reserve(_favoriteAppearances.size()); + for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end(); ++itr) + if (itr->second != FavoriteAppearanceState::Removed) + transmogCollectionUpdate.FavoriteAppearances.push_back(itr->first); + + _owner->SendPacket(transmogCollectionUpdate.Write()); +} diff --git a/src/server/game/Entities/Player/CollectionMgr.h b/src/server/game/Entities/Player/CollectionMgr.h index 1b995be3646..2cb11bdab8b 100644 --- a/src/server/game/Entities/Player/CollectionMgr.h +++ b/src/server/game/Entities/Player/CollectionMgr.h @@ -19,6 +19,7 @@ #define CollectionMgr_h__ #include "WorldSession.h" +#include <boost/dynamic_bitset.hpp> enum HeirloomPlayerFlags { @@ -48,7 +49,7 @@ typedef std::map<uint32, HeirloomData> HeirloomContainer; class TC_GAME_API CollectionMgr { public: - explicit CollectionMgr(WorldSession* owner) : _owner(owner) { } + explicit CollectionMgr(WorldSession* owner); WorldSession* GetOwner() const { return _owner; } @@ -64,6 +65,8 @@ public: ToyBoxContainer const& GetAccountToys() const { return _toys; } + void OnItemAdded(Item* item); + // Account-wide heirlooms void LoadHeirlooms(); void LoadAccountHeirlooms(PreparedQueryResult result); @@ -79,11 +82,39 @@ public: // Account-wide mounts + // Appearances + void LoadItemAppearances(); + void LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances); + void SaveAccountItemAppearances(SQLTransaction& trans); + void AddItemAppearance(Item* item); + void AddItemAppearance(uint32 itemId); + void RemoveTemporaryAppearance(Item* item); + // returns pair<hasAppearance, isTemporary> + std::pair<bool, bool> HasItemAppearance(uint32 itemModifiedAppearanceId) const; + std::unordered_set<ObjectGuid> GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const; + + enum class FavoriteAppearanceState + { + New, + Removed, + Unchanged + }; + + void SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply); + void SendFavoriteAppearances() const; + private: + bool CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const; + void AddItemAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance); + void AddTemporaryAppearance(ObjectGuid const& itemGuid, ItemModifiedAppearanceEntry const* itemModifiedAppearance); + WorldSession* _owner; ToyBoxContainer _toys; HeirloomContainer _heirlooms; + boost::dynamic_bitset<uint32> _appearances; + std::unordered_map<uint32, std::unordered_set<ObjectGuid>> _temporaryAppearances; + std::unordered_map<uint32, FavoriteAppearanceState> _favoriteAppearances; }; #endif // CollectionMgr_h__ diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 388fb2b6bab..a59624515dd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -88,6 +88,7 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "TalentPackets.h" +#include "TransmogrificationPackets.h" #include "ToyPackets.h" #include "Transport.h" #include "UpdateData.h" @@ -3963,6 +3964,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt64(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG_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); @@ -4345,7 +4350,7 @@ Corpse* Player::CreateCorpse() { if (m_items[i]) { - iDisplayID = m_items[i]->GetDisplayId(); + iDisplayID = m_items[i]->GetDisplayId(this); iIventoryType = m_items[i]->GetTemplate()->GetInventoryType(); _cfi = iDisplayID | (iIventoryType << 24); @@ -10930,9 +10935,6 @@ InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const return EQUIP_ERR_PROFICIENCY_NEEDED; } - if (pProto->GetRequiredReputationFaction() && uint32(GetReputationRank(pProto->GetRequiredReputationFaction())) < pProto->GetRequiredReputationRank()) - return EQUIP_ERR_CANT_EQUIP_REPUTATION; - return EQUIP_ERR_OK; } } @@ -10946,6 +10948,9 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const if (!proto) return EQUIP_ERR_ITEM_NOT_FOUND; + if (proto->GetFlags2() & ITEM_FLAG2_UNAVAILABLE_FOR_PLAYERS) + return EQUIP_ERR_CANT_EQUIP_EVER; + if ((proto->GetFlags2() & ITEM_FLAG2_HORDE_ONLY) && GetTeam() != HORDE) return EQUIP_ERR_CANT_EQUIP_EVER; @@ -10973,6 +10978,9 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const if (proto->GetHolidayID() && !IsHolidayActive(proto->GetHolidayID())) return EQUIP_ERR_CLIENT_LOCKED_OUT; + if (proto->GetRequiredReputationFaction() && uint32(GetReputationRank(proto->GetRequiredReputationFaction())) < proto->GetRequiredReputationRank()) + return EQUIP_ERR_CANT_EQUIP_REPUTATION; + // learning (recipes, mounts, pets, etc.) if (proto->Effects.size() >= 2) if (proto->Effects[0]->SpellID == 483 || proto->Effects[0]->SpellID == 55884) @@ -11015,7 +11023,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje if (proto->GetClass() == ITEM_CLASS_WEAPON && GetSkillValue(proto->GetSkill()) == 0) return EQUIP_ERR_PROFICIENCY_NEEDED; - if (proto->GetClass() == ITEM_CLASS_ARMOR && proto->GetSubClass() > ITEM_SUBCLASS_ARMOR_MISCELLANEOUS && proto->GetSubClass() < ITEM_SUBCLASS_ARMOR_BUCKLER && proto->GetInventoryType() != INVTYPE_CLOAK) + if (proto->GetClass() == ITEM_CLASS_ARMOR && proto->GetSubClass() > ITEM_SUBCLASS_ARMOR_MISCELLANEOUS && proto->GetSubClass() < ITEM_SUBCLASS_ARMOR_COSMETIC && proto->GetInventoryType() != INVTYPE_CLOAK) { if (_class == CLASS_WARRIOR || _class == CLASS_PALADIN || _class == CLASS_DEATH_KNIGHT) { @@ -11051,7 +11059,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje } // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case. -Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId /*= 0*/, GuidSet const& allowedLooters /*= GuidSet()*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/) +Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId /*= 0*/, GuidSet const& allowedLooters /*= GuidSet()*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/, bool addToCollection /*= true*/) { uint32 count = 0; for (ItemPosCountVec::const_iterator itr = pos.begin(); itr != pos.end(); ++itr) @@ -11064,9 +11072,6 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat UpdateCriteria(CRITERIA_TYPE_RECEIVE_EPIC_ITEM, itemId, count); UpdateCriteria(CRITERIA_TYPE_OWN_ITEM, itemId, 1); - if (sDB2Manager.GetHeirloomByItemId(itemId)) - GetSession()->GetCollectionMgr()->AddHeirloom(itemId, 0); - if (randomPropertyId) item->SetItemRandomProperties(randomPropertyId); @@ -11093,6 +11098,9 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat stmt->setString(1, ss.str()); CharacterDatabase.Execute(stmt); } + + if (addToCollection) + GetSession()->GetCollectionMgr()->OnItemAdded(item); } return item; } @@ -11376,9 +11384,9 @@ void Player::SetVisibleItemSlot(uint8 slot, Item* pItem) { if (pItem) { - SetUInt32Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENTRY_OFFSET + (slot * 2), pItem->GetVisibleEntry()); - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 0, pItem->GetVisibleAppearanceModId()); - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 1, pItem->GetVisibleItemVisual()); + SetUInt32Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENTRY_OFFSET + (slot * 2), pItem->GetVisibleEntry(this)); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 0, pItem->GetVisibleAppearanceModId(this)); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 1, pItem->GetVisibleItemVisual(this)); } else { @@ -11394,7 +11402,10 @@ void Player::VisualizeItem(uint8 slot, Item* pItem) // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if (pItem->GetTemplate()->GetBonding() == BIND_WHEN_EQUIPED || pItem->GetTemplate()->GetBonding() == BIND_WHEN_PICKED_UP || pItem->GetTemplate()->GetBonding() == BIND_QUEST_ITEM) + { pItem->SetBinding(true); + GetSession()->GetCollectionMgr()->AddItemAppearance(pItem); + } TC_LOG_DEBUG("entities.player.items", "Player::SetVisibleItemSlot: Player '%s' (%s), Slot: %u, Item: %u", GetName().c_str(), GetGUID().ToString().c_str(), slot, pItem->GetEntry()); @@ -11505,8 +11516,9 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update) { ItemRemovedQuestCheck(it->GetEntry(), it->GetCount()); RemoveItem(bag, slot, update); - it->SetNotRefundable(this, false); + it->SetNotRefundable(this, false, nullptr, false); it->RemoveFromUpdateQueueOf(this); + GetSession()->GetCollectionMgr()->RemoveTemporaryAppearance(it); if (it->IsInWorld()) { it->RemoveFromWorld(); @@ -13118,7 +13130,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool // visualize enchantment at player and equipped items if (slot == PERM_ENCHANTMENT_SLOT) - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (item->GetSlot() * 2), 1, item->GetVisibleItemVisual()); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (item->GetSlot() * 2), 1, item->GetVisibleItemVisual(this)); if (apply_dur) { @@ -16874,6 +16886,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); GetSession()->GetCollectionMgr()->LoadToys(); GetSession()->GetCollectionMgr()->LoadHeirlooms(); + GetSession()->GetCollectionMgr()->LoadItemAppearances(); LearnSpecializationSpells(); @@ -17271,10 +17284,16 @@ void Player::LoadCorpse(PreparedQueryResult result) void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - // SELECT ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId - // 15 16 17 18 19 20 21 22 23 24 25 - // ii.enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, ii.bonusListIDs, ig.gemItemId1, ig.gemItemId2, ig.gemItemId3, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot + // 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, + // 13 14 15 16 17 18 + // upgradeId, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, + // 19 20 21 22 23 + // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, + // 24 25 26 27 28 + // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, + // 29 30 31 32 33 + // gemItemId1, gemItemId2, gemItemId3, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot //NOTE: the "order by `bag`" is important because it makes sure //the bagMap is filled before items in the bags are loaded //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?) @@ -17296,10 +17315,11 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) Field* fields = result->Fetch(); if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields)) { - ObjectGuid bagGuid = fields[24].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[24].GetUInt64()) : ObjectGuid::Empty; - uint8 slot = fields[25].GetUInt8(); + ObjectGuid bagGuid = fields[32].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[32].GetUInt64()) : ObjectGuid::Empty; + uint8 slot = fields[33].GetUInt8(); GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item); + GetSession()->GetCollectionMgr()->AddItemAppearance(item); InventoryResult err = EQUIP_ERR_OK; // Item is not in bag @@ -17625,7 +17645,7 @@ void Player::_LoadMailedItems(Mail* mail) Item* item = NewItemOrBag(proto); - ObjectGuid ownerGuid = fields[24].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[24].GetUInt64()) : ObjectGuid::Empty; + ObjectGuid ownerGuid = fields[32].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[32].GetUInt64()) : ObjectGuid::Empty; if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry)) { TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: " UI64FMTD ") in mail (%u) doesn't exist, deleted from mail.", itemGuid, mail->messageID); @@ -17861,6 +17881,18 @@ void Player::_LoadQuestStatusRewarded(PreparedQueryResult result) if (!quest->IsDailyOrWeekly() && !quest->IsMonthly() && !quest->IsSeasonal()) if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(quest_id)) SetQuestCompletedBit(questBit, true); + + for (uint32 i = 0; i < quest->GetRewChoiceItemsCount(); ++i) + GetSession()->GetCollectionMgr()->AddItemAppearance(quest->RewardChoiceItemId[i]); + + for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i) + GetSession()->GetCollectionMgr()->AddItemAppearance(quest->RewardItemId[i]); + + if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(quest->GetQuestPackageID())) + for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) + if (ItemTemplate const* rewardProto = sObjectMgr->GetItemTemplate(questPackageItem->ItemID)) + if (rewardProto->ItemSpecClassMask & getClassMask()) + GetSession()->GetCollectionMgr()->AddItemAppearance(questPackageItem->ItemID); } m_RewardedQuests.insert(quest_id); @@ -18649,8 +18681,8 @@ void Player::SaveToDB(bool create /*=false*/) { if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId() << ' '; - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId())) + ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId(this) << ' '; + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this))) ss << enchant->ItemVisual; else ss << '0'; @@ -18790,8 +18822,8 @@ void Player::SaveToDB(bool create /*=false*/) { if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId() << ' '; - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId())) + ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId(this) << ' '; + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this))) ss << enchant->ItemVisual; else ss << '0'; @@ -18860,6 +18892,7 @@ void Player::SaveToDB(bool create /*=false*/) GetSession()->GetCollectionMgr()->SaveAccountToys(trans); GetSession()->GetBattlePetMgr()->SaveToDB(trans); GetSession()->GetCollectionMgr()->SaveAccountHeirlooms(trans); + GetSession()->GetCollectionMgr()->SaveAccountItemAppearances(trans); stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_LAST_PLAYER_CHARACTERS); stmt->setUInt32(0, GetSession()->GetAccountId()); @@ -20232,7 +20265,7 @@ void Player::VehicleSpellInitialize() petSpells.TimeLimit = vehicle->IsSummon() ? vehicle->ToTempSummon()->GetTimer() : 0; petSpells.ReactState = vehicle->GetReactState(); petSpells.CommandState = COMMAND_FOLLOW; - petSpells.Flag = 0x800; + petSpells.Flag = 0x8; for (uint32 i = 0; i < MAX_SPELL_CONTROL_BAR; ++i) petSpells.ActionButtons[i] = MAKE_UNIT_ACTION_BUTTON(0, i + 8); @@ -20993,7 +21026,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c } Item* it = bStore ? - StoreNewItem(vDest, item, true) : + StoreNewItem(vDest, item, true, Item::GenerateItemRandomPropertyId(item), {}, {}, false) : EquipNewItem(uiDest, item, true); if (it) { @@ -21020,6 +21053,8 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c it->SaveRefundDataToDB(); AddRefundReference(it->GetGUID()); } + + GetSession()->GetCollectionMgr()->OnItemAdded(it); } return true; } @@ -22293,6 +22328,8 @@ void Player::SendInitialPacketsBeforeAddToMap() heirloomUpdate.Heirlooms = &GetSession()->GetCollectionMgr()->GetAccountHeirlooms(); SendDirectMessage(heirloomUpdate.Write()); + GetSession()->GetCollectionMgr()->SendFavoriteAppearances(); + WorldPackets::Character::InitialSetup initialSetup; initialSetup.ServerExpansionLevel = sWorld->getIntConfig(CONFIG_EXPANSION); SendDirectMessage(initialSetup.Write()); @@ -25199,6 +25236,10 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) SetPower(pw, 0); UpdateItemSetAuras(false); + // update visible transmog + for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) + if (Item* equippedItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + SetVisibleItemSlot(i, equippedItem); } void Player::ResetTimeSync() @@ -25411,7 +25452,8 @@ void Player::RefundItem(Item* item) SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Delete any references to the refund data - item->SetNotRefundable(this, true, &trans); + item->SetNotRefundable(this, true, &trans, false); + GetSession()->GetCollectionMgr()->RemoveTemporaryAppearance(item); // Destroy item DestroyItem(item->GetBagSlot(), item->GetSlot(), true); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3969e026aee..4f2df580d54 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1366,7 +1366,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasItemTotemCategory(uint32 TotemCategory) const; InventoryResult CanUseItem(ItemTemplate const* pItem) const; InventoryResult CanRollForItemInLFG(ItemTemplate const* item, WorldObject const* lootedObject) const; - Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId = 0, GuidSet const& allowedLooters = GuidSet(), std::vector<int32> const& bonusListIDs = std::vector<int32>()); + Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId = 0, GuidSet const& allowedLooters = GuidSet(), std::vector<int32> const& bonusListIDs = std::vector<int32>(), bool addToCollection = true); Item* StoreItem(ItemPosCountVec const& pos, Item* pItem, bool update); Item* EquipNewItem(uint16 pos, uint32 item, bool update); Item* EquipItem(uint16 pos, Item* pItem, bool update); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1466deda61d..0ea0f832405 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2666,6 +2666,7 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.MaxMoneyLoot = 0; itemTemplate.FlagsCu = 0; itemTemplate.SpellPPMRate = 0.0f; + itemTemplate.ItemSpecClassMask = 0; if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID)) { @@ -2699,8 +2700,13 @@ void ObjectMgr::LoadItemTemplates() continue; if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpec->SpecID)) + { if ((1 << (specialization->ClassID - 1)) & sparse->AllowableClass) + { + itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1); itemTemplate.Specializations[itemSpec->MaxLevel > 40].insert(itemSpec->SpecID); + } + } } } } diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index b09b75be882..2bd97bd7728 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -2394,7 +2394,7 @@ void Guild::LoadBankTabFromDB(Field* fields) bool Guild::LoadBankItemFromDB(Field* fields) { - uint8 tabId = fields[25].GetUInt8(); + uint8 tabId = fields[33].GetUInt8(); if (tabId >= _GetPurchasedTabsSize()) { TC_LOG_ERROR("guild", "Invalid tab for item (GUID: %u, id: #%u) in guild bank, skipped.", diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp index 20cbf22d40e..48216a2809a 100644 --- a/src/server/game/Guilds/GuildMgr.cpp +++ b/src/server/game/Guilds/GuildMgr.cpp @@ -401,10 +401,16 @@ void GuildMgr::LoadGuilds() // Delete orphan guild bank items CharacterDatabase.DirectExecute("DELETE gbi FROM guild_bank_item gbi LEFT JOIN guild g ON gbi.guildId = g.guildId WHERE g.guildId IS NULL"); - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, - // 17 18 19 20 21 22 23 24 25 26 - // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, ig.gemItemId1, ig.gemItemId2, ig.gemItemId3, guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, upgradeId, + // 14 15 16 17 18 + // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, + // 19 20 21 22 23 + // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, + // 24 25 26 27 28 + // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, + // 29 30 31 32 33 + // gemItemId1, gemItemId2, gemItemId3, guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_BANK_ITEMS)); if (!result) @@ -417,7 +423,7 @@ void GuildMgr::LoadGuilds() do { Field* fields = result->Fetch(); - uint64 guildId = fields[24].GetUInt64(); + uint64 guildId = fields[32].GetUInt64(); if (Guild* guild = GetGuildById(guildId)) guild->LoadBankItemFromDB(fields); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 63a37b42efd..c3bea2abb34 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1653,10 +1653,16 @@ void WorldSession::HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipm else { saveEquipmentSet.Set.Pieces[i].Clear(); - if (saveEquipmentSet.Set.Appearances[i] && !sItemModifiedAppearanceStore.LookupEntry(saveEquipmentSet.Set.Appearances[i])) - return; + if (saveEquipmentSet.Set.Appearances[i]) + { + if (!sItemModifiedAppearanceStore.LookupEntry(saveEquipmentSet.Set.Appearances[i])) + return; - // TODO: validata whether appearance is known + bool hasAppearance, isTemporary; + std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(saveEquipmentSet.Set.Appearances[i]); + if (!hasAppearance) + return; + } } } else @@ -1672,6 +1678,33 @@ void WorldSession::HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipm saveEquipmentSet.Set.Enchants[0] = 0; saveEquipmentSet.Set.Enchants[1] = 0; } + else + { + auto validateIllusion = [this](uint32 enchantId) -> bool + { + SpellItemEnchantmentEntry const* illusion = sSpellItemEnchantmentStore.LookupEntry(enchantId); + if (!illusion) + return false; + + if (!illusion->ItemVisual || !(illusion->Flags & ENCHANTMENT_COLLECTABLE)) + return false; + + if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(illusion->PlayerConditionID)) + if (!sConditionMgr->IsPlayerMeetingCondition(_player, condition)) + return false; + + if (illusion->ScalingClassRestricted > 0 && uint8(illusion->ScalingClassRestricted) != _player->getClass()) + return false; + + return true; + }; + + if (saveEquipmentSet.Set.Enchants[0] && !validateIllusion(saveEquipmentSet.Set.Enchants[0])) + return; + + if (saveEquipmentSet.Set.Enchants[1] && !validateIllusion(saveEquipmentSet.Set.Enchants[1])) + return; + } _player->SetEquipmentSet(saveEquipmentSet.Set); } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index b5c8e6bdc67..938523afb8a 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -1096,164 +1096,6 @@ void WorldSession::HandleItemRefund(WorldPackets::Item::ItemPurchaseRefund& pack GetPlayer()->RefundItem(item); } -void WorldSession::HandleTransmogrifyItems(WorldPackets::Item::TransmogrifyItems& transmogrifyItems) -{ - Player* player = GetPlayer(); - // Validate - if (!player->GetNPCIfCanInteractWith(transmogrifyItems.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER)) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", transmogrifyItems.Npc.ToString().c_str()); - return; - } - - int64 cost = 0; - std::unordered_map<Item*, Item*> transmogItems; - std::unordered_map<Item*, std::pair<VoidStorageItem*, BonusData>> transmogVoidItems; - std::vector<Item*> resetAppearanceItems; - - for (WorldPackets::Item::TransmogrifyItem const& transmogItem : transmogrifyItems.Items) - { - // slot of the transmogrified item - if (transmogItem.Slot >= EQUIPMENT_SLOT_END) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify wrong slot (%u) when transmogrifying items.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); - return; - } - - // transmogrified item - Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot); - if (!itemTransmogrified) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); - return; - } - if (player->CanUseItem(itemTransmogrified->GetTemplate()) != EQUIP_ERR_OK) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an unequippable item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); - return; - } - - WorldPackets::Item::ItemInstance itemInstance; - BonusData const* bonus = nullptr; - if (transmogItem.SrcItemGUID) - { - // guid of the transmogrifier item - Item* itemTransmogrifier = player->GetItemByGuid(*transmogItem.SrcItemGUID); - if (!itemTransmogrifier) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str()); - return; - } - if (player->CanUseItem(itemTransmogrifier->GetTemplate()) != EQUIP_ERR_OK) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an unequippable item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str()); - return; - } - - itemInstance.Initialize(itemTransmogrifier); - bonus = itemTransmogrifier->GetBonus(); - transmogItems[itemTransmogrified] = itemTransmogrifier; - } - else if (transmogItem.SrcVoidItemGUID) - { - // guid of the transmogrifier item - uint8 slot; - VoidStorageItem* itemTransmogrifier = player->GetVoidStorageItem(transmogItem.SrcVoidItemGUID->GetCounter(), slot); - if (!itemTransmogrifier) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid void storage item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcVoidItemGUID->ToString().c_str()); - return; - } - ItemTemplate const * transmogrifierTemplate = sObjectMgr->GetItemTemplate(itemTransmogrifier->ItemEntry); - if (player->CanUseItem(transmogrifierTemplate) != EQUIP_ERR_OK) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an unequippable void storage item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcVoidItemGUID->ToString().c_str()); - return; - } - - itemInstance.Initialize(itemTransmogrifier); - std::pair<VoidStorageItem*, BonusData>& transmogData = transmogVoidItems[itemTransmogrified]; - transmogData.first = itemTransmogrifier; - transmogData.second.Initialize(itemInstance); - bonus = &transmogData.second; - } - else - { - resetAppearanceItems.push_back(itemTransmogrified); - continue; - } - - // entry of transmogrifier and from packet - if (itemInstance != transmogItem.Item) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item instance data for %s.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str()); - return; - } - - // validity of the transmogrification items - if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, transmogItem.Item, bonus)) - { - TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), itemTransmogrified->GetEntry(), transmogItem.Item.ItemID); - return; - } - - // add cost - cost += itemTransmogrified->GetSpecialPrice(); - } - - if (cost) // 0 cost if reverting look - { - if (!player->HasEnoughMoney(cost)) - return; - player->ModifyMoney(-cost); - } - - // Everything is fine, proceed - for (auto& transmogPair : transmogItems) - { - Item* transmogrified = transmogPair.first; - Item* transmogrifier = transmogPair.second; - - transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->GetEntry()); - transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, transmogrifier->GetAppearanceModId()); - player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified); - - transmogrified->SetNotRefundable(player); - transmogrified->ClearSoulboundTradeable(player); - - transmogrifier->SetNotRefundable(player); - transmogrifier->ClearSoulboundTradeable(player); - - if (transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_EQUIPED || transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_USE) - transmogrifier->SetBinding(true); - - transmogrified->SetState(ITEM_CHANGED, player); - } - - for (auto& transmogVoirPair : transmogVoidItems) - { - Item* transmogrified = transmogVoirPair.first; - VoidStorageItem* transmogrifier = transmogVoirPair.second.first; - BonusData& bonus = transmogVoirPair.second.second; - - transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->ItemEntry); - transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, bonus.AppearanceModID); - player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified); - - transmogrified->SetNotRefundable(player); - transmogrified->ClearSoulboundTradeable(player); - transmogrified->SetState(ITEM_CHANGED, player); - } - - for (Item* item : resetAppearanceItems) - { - item->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, 0); - item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, 0); - item->SetState(ITEM_CHANGED, player); - player->SetVisibleItemSlot(item->GetSlot(), item); - } -} - bool WorldSession::CanUseBank(ObjectGuid bankerGUID) const { // bankerGUID parameter is optional, set to 0 by default. diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 8ce4a88ef5a..4bfada8aab3 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -17,6 +17,7 @@ */ #include "WorldSession.h" +#include "CollectionMgr.h" #include "Common.h" #include "Config.h" #include "GameObjectAI.h" @@ -111,6 +112,7 @@ void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet) { item->SetState(ITEM_CHANGED, user); item->SetBinding(true); + GetCollectionMgr()->AddItemAppearance(item); } } @@ -544,7 +546,7 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI (slot == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))) itemDisplayId = 0; else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) - itemDisplayId = item->GetDisplayId(); + itemDisplayId = item->GetDisplayId(player); else itemDisplayId = 0; diff --git a/src/server/game/Handlers/TransmogrificationHandler.cpp b/src/server/game/Handlers/TransmogrificationHandler.cpp new file mode 100644 index 00000000000..0d8406beb45 --- /dev/null +++ b/src/server/game/Handlers/TransmogrificationHandler.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://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 "CollectionMgr.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "TransmogrificationPackets.h" + +void WorldSession::HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems) +{ + Player* player = GetPlayer(); + // Validate + if (!player->GetNPCIfCanInteractWith(transmogrifyItems.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER)) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", transmogrifyItems.Npc.ToString().c_str()); + return; + } + + int64 cost = 0; + std::unordered_map<Item*, uint32> transmogItems; + std::unordered_map<Item*, uint32> illusionItems; + + std::vector<Item*> resetAppearanceItems; + std::vector<Item*> resetIllusionItems; + std::vector<uint32> bindAppearances; + + for (WorldPackets::Transmogrification::TransmogrifyItem const& transmogItem : transmogrifyItems.Items) + { + // slot of the transmogrified item + if (transmogItem.Slot >= EQUIPMENT_SLOT_END) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify wrong slot (%u) when transmogrifying items.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); + return; + } + + // transmogrified item + Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot); + if (!itemTransmogrified) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); + return; + } + + if (transmogItem.ItemModifiedAppearanceID) + { + ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(transmogItem.ItemModifiedAppearanceID); + if (!itemModifiedAppearance) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using invalid appearance (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID); + return; + } + + bool hasAppearance, isTemporary; + std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(transmogItem.ItemModifiedAppearanceID); + if (!hasAppearance) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using appearance he has not collected (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID); + return; + } + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID); + if (player->CanUseItem(itemTemplate) != EQUIP_ERR_OK) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using appearance he can never use (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID); + return; + } + + // validity of the transmogrification items + if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemModifiedAppearance)) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s failed CanTransmogrifyItemWithItem (%u with appearance %d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), itemTransmogrified->GetEntry(), transmogItem.ItemModifiedAppearanceID); + return; + } + + transmogItems[itemTransmogrified] = transmogItem.ItemModifiedAppearanceID; + if (isTemporary) + bindAppearances.push_back(transmogItem.ItemModifiedAppearanceID); + + // add cost + cost += itemTransmogrified->GetSpecialPrice(); + } + else + resetAppearanceItems.push_back(itemTransmogrified); + + if (transmogItem.SpellItemEnchantmentID) + { + if (transmogItem.Slot != EQUIPMENT_SLOT_MAINHAND && transmogItem.Slot != EQUIPMENT_SLOT_OFFHAND) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion into non-weapon slot (%u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot); + return; + } + + SpellItemEnchantmentEntry const* illusion = sSpellItemEnchantmentStore.LookupEntry(transmogItem.SpellItemEnchantmentID); + if (!illusion) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using invalid enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID); + return; + } + + if (!illusion->ItemVisual || !(illusion->Flags & ENCHANTMENT_COLLECTABLE)) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not allowed enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID); + return; + } + + if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(illusion->PlayerConditionID)) + { + if (!sConditionMgr->IsPlayerMeetingCondition(player, condition)) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not collected enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID); + return; + } + } + + if (illusion->ScalingClassRestricted > 0 && uint8(illusion->ScalingClassRestricted) != player->getClass()) + { + TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not allowed class enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID); + return; + } + + illusionItems[itemTransmogrified] = transmogItem.SpellItemEnchantmentID; + cost += illusion->TransmogCost; + } + else + resetIllusionItems.push_back(itemTransmogrified); + } + + if (cost) // 0 cost if reverting look + { + if (!player->HasEnoughMoney(cost)) + return; + player->ModifyMoney(-cost); + } + + // Everything is fine, proceed + for (auto& transmogPair : transmogItems) + { + Item* transmogrified = transmogPair.first; + + if (!transmogrifyItems.CurrentSpecOnly) + { + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, transmogPair.second); + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0); + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0); + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0); + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0); + } + else + { + if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1)) + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2)) + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3)) + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4)) + transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + transmogrified->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second); + } + + player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified); + + transmogrified->SetNotRefundable(player); + transmogrified->ClearSoulboundTradeable(player); + transmogrified->SetState(ITEM_CHANGED, player); + } + + for (auto& illusionPair : illusionItems) + { + Item* transmogrified = illusionPair.first; + + if (!transmogrifyItems.CurrentSpecOnly) + { + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, illusionPair.second); + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0); + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0); + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0); + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0); + } + else + { + if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1)) + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2)) + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3)) + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4)) + transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + transmogrified->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], illusionPair.second); + } + + player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified); + + transmogrified->SetNotRefundable(player); + transmogrified->ClearSoulboundTradeable(player); + transmogrified->SetState(ITEM_CHANGED, player); + } + + for (Item* item : resetAppearanceItems) + { + if (!transmogrifyItems.CurrentSpecOnly) + { + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, 0); + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0); + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0); + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0); + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0); + } + else + { + if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1)) + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2)) + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3)) + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4)) + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + item->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0); + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0); + } + + item->SetState(ITEM_CHANGED, player); + player->SetVisibleItemSlot(item->GetSlot(), item); + } + + for (Item* item : resetIllusionItems) + { + if (!transmogrifyItems.CurrentSpecOnly) + { + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0); + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0); + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0); + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0); + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0); + } + else + { + if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1)) + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2)) + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3)) + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4)) + item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + item->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], 0); + item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, 0); + } + + item->SetState(ITEM_CHANGED, player); + player->SetVisibleItemSlot(item->GetSlot(), item); + } + + for (uint32 itemModifedAppearanceId : bindAppearances) + { + std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId); + for (ObjectGuid const& itemGuid : itemsProvidingAppearance) + { + if (Item* item = player->GetItemByGuid(itemGuid)) + { + item->SetNotRefundable(player); + item->ClearSoulboundTradeable(player); + GetCollectionMgr()->AddItemAppearance(item); + } + } + } +} + +void WorldSession::HandleTransmogAppearanceSetFavorite(WorldPackets::Transmogrification::TransmogAppearanceSetFavorite& transmogAppearanceSetFavorite) +{ + bool hasAppearance, isTemporary; + std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(transmogAppearanceSetFavorite.ItemModifiedAppearanceID); + if (!hasAppearance || isTemporary) + return; + + GetCollectionMgr()->SetAppearanceIsFavorite(transmogAppearanceSetFavorite.ItemModifiedAppearanceID, transmogAppearanceSetFavorite.IsFavorite); +} diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp index 637566d894a..6575f7b045c 100644 --- a/src/server/game/Handlers/VoidStorageHandler.cpp +++ b/src/server/game/Handlers/VoidStorageHandler.cpp @@ -16,6 +16,7 @@ */ #include "Common.h" +#include "CollectionMgr.h" #include "WorldPacket.h" #include "WorldSession.h" #include "World.h" @@ -196,6 +197,7 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor item->SetGuidValue(ITEM_FIELD_CREATOR, itemVS->CreatorGuid); item->SetModifier(ITEM_MODIFIER_UPGRADE_ID, itemVS->ItemUpgradeId); item->SetBinding(true); + GetCollectionMgr()->AddItemAppearance(item); voidStorageTransferChanges.RemovedItems.push_back(ObjectGuid::Create<HighGuid::Item>(itemVS->ItemId)); diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 2c2aa528bb1..932b38bbd5e 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -70,6 +70,7 @@ #include "TotemPackets.h" #include "ToyPackets.h" #include "TradePackets.h" +#include "TransmogrificationPackets.h" #include "VehiclePackets.h" #include "VoicePackets.h" #include "VoidStoragePackets.h" diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp index ffaa7f0a88b..3ae94dfb84a 100644 --- a/src/server/game/Server/Packets/ItemPackets.cpp +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -503,36 +503,6 @@ WorldPacket const* WorldPackets::Item::ItemEnchantTimeUpdate::Write() return &_worldPacket; } -ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Item::TransmogrifyItem& transmogItem) -{ - if (data.ReadBit()) - transmogItem.SrcItemGUID = boost::in_place(); - - if (data.ReadBit()) - transmogItem.SrcVoidItemGUID = boost::in_place(); - - data >> transmogItem.Item; - data >> transmogItem.Slot; - - if (transmogItem.SrcItemGUID.is_initialized()) - data >> *transmogItem.SrcItemGUID; - - if (transmogItem.SrcVoidItemGUID.is_initialized()) - data >> *transmogItem.SrcVoidItemGUID; - - return data; -} - -void WorldPackets::Item::TransmogrifyItems::Read() -{ - Items.resize(_worldPacket.read<uint32>()); - _worldPacket >> Npc; - for (TransmogrifyItem& item : Items) - _worldPacket >> item; - - CurrentSpecOnly = _worldPacket.ReadBit(); -} - void WorldPackets::Item::UseCritterItem::Read() { _worldPacket >> ItemGuid; diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h index df2e5c9e1d0..384ba7eea5b 100644 --- a/src/server/game/Server/Packets/ItemPackets.h +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -474,30 +474,6 @@ namespace WorldPackets uint32 Slot = 0; }; - struct TransmogrifyItem - { - Optional<ObjectGuid> SrcItemGUID; - Optional<ObjectGuid> SrcVoidItemGUID; - ItemInstance Item; - uint32 Slot = 0; - }; - - class TransmogrifyItems final : public ClientPacket - { - public: - enum - { - MAX_TRANSMOGRIFY_ITEMS = 11 - }; - - TransmogrifyItems(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOGRIFY_ITEMS, std::move(packet)) { } - - void Read() override; - - ObjectGuid Npc; - Array<TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS> Items; - }; - class UseCritterItem final : public ClientPacket { public: diff --git a/src/server/game/Server/Packets/TransmogrificationPackets.cpp b/src/server/game/Server/Packets/TransmogrificationPackets.cpp new file mode 100644 index 00000000000..55d4cec7c98 --- /dev/null +++ b/src/server/game/Server/Packets/TransmogrificationPackets.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://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 "TransmogrificationPackets.h" + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Transmogrification::TransmogrifyItem& transmogItem) +{ + data >> transmogItem.ItemModifiedAppearanceID; + data >> transmogItem.Slot; + data >> transmogItem.SpellItemEnchantmentID; + + return data; +} + +void WorldPackets::Transmogrification::TransmogrifyItems::Read() +{ + Items.resize(_worldPacket.read<uint32>()); + _worldPacket >> Npc; + for (TransmogrifyItem& item : Items) + _worldPacket >> item; + + CurrentSpecOnly = _worldPacket.ReadBit(); +} + +void WorldPackets::Transmogrification::TransmogAppearanceSetFavorite::Read() +{ + _worldPacket >> ItemModifiedAppearanceID; + IsFavorite = _worldPacket.ReadBit(); +} + +WorldPacket const* WorldPackets::Transmogrification::TransmogCollectionUpdate::Write() +{ + _worldPacket.WriteBit(IsFullUpdate); + _worldPacket.WriteBit(IsSetFavorite); + _worldPacket << uint32(FavoriteAppearances.size()); + for (uint32 itemModifiedAppearanceId : FavoriteAppearances) + _worldPacket << uint32(itemModifiedAppearanceId); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/TransmogrificationPackets.h b/src/server/game/Server/Packets/TransmogrificationPackets.h new file mode 100644 index 00000000000..93305b1e0bf --- /dev/null +++ b/src/server/game/Server/Packets/TransmogrificationPackets.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TransmogrificationPackets_h__ +#define TransmogrificationPackets_h__ + +#include "Packet.h" +#include "ObjectGuid.h" +#include "PacketUtilities.h" + +namespace WorldPackets +{ + namespace Transmogrification + { + struct TransmogrifyItem + { + int32 ItemModifiedAppearanceID = 0; + uint32 Slot = 0; + int32 SpellItemEnchantmentID = 0; + }; + + class TransmogrifyItems final : public ClientPacket + { + public: + enum + { + MAX_TRANSMOGRIFY_ITEMS = 13 + }; + + TransmogrifyItems(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOGRIFY_ITEMS, std::move(packet)) { } + + void Read() override; + + ObjectGuid Npc; + Array<TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS> Items; + bool CurrentSpecOnly = false; + }; + + class TransmogAppearanceSetFavorite final : public ClientPacket + { + public: + TransmogAppearanceSetFavorite(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, std::move(packet)) { } + + void Read() override; + + uint32 ItemModifiedAppearanceID = 0; + bool IsFavorite = false; + }; + + class TransmogCollectionUpdate final : public ServerPacket + { + public: + TransmogCollectionUpdate() : ServerPacket(SMSG_TRANSMOG_COLLECTION_UPDATE) { } + + WorldPacket const* Write() override; + + bool IsFullUpdate = false; + bool IsSetFavorite = false; + std::vector<uint32> FavoriteAppearances; + }; + } +} + +#endif // TransmogrificationPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index ac47130dcee..777f69c1c17 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -774,8 +774,8 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_TRADE_SKILL_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_TRAINER_BUY_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::TrainerBuySpell, &WorldSession::HandleTrainerBuySpellOpcode); DEFINE_HANDLER(CMSG_TRAINER_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleTrainerListOpcode); - DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Item::TransmogrifyItems, &WorldSession::HandleTransmogrifyItems); - DEFINE_HANDLER(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Transmogrification::TransmogrifyItems, &WorldSession::HandleTransmogrifyItems); + DEFINE_HANDLER(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Transmogrification::TransmogAppearanceSetFavorite, &WorldSession::HandleTransmogAppearanceSetFavorite); DEFINE_HANDLER(CMSG_TURN_IN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Petition::TurnInPetition, &WorldSession::HandleTurnInPetition); DEFINE_HANDLER(CMSG_TUTORIAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::TutorialSetFlag, &WorldSession::HandleTutorialFlag); DEFINE_HANDLER(CMSG_TWITTER_CHECK_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -1710,7 +1710,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRAINER_LIST, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_ABORTED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_PENDING, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_COLLECTION_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_COLLECTION_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_MOVIE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TURN_IN_PETITION_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 937961ad571..a013d8bd662 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1079,6 +1079,8 @@ public: BATTLE_PET_SLOTS, GLOBAL_ACCOUNT_HEIRLOOMS, GLOBAL_REALM_CHARACTER_COUNTS, + ITEM_APPEARANCES, + ITEM_FAVORITE_APPEARANCES, MAX_QUERIES }; @@ -1109,6 +1111,14 @@ public: stmt->setUInt32(0, accountId); ok = SetPreparedQuery(GLOBAL_REALM_CHARACTER_COUNTS, stmt) && ok; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ITEM_APPEARANCES); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(ITEM_APPEARANCES, stmt) && ok; + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ITEM_FAVORITE_APPEARANCES); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(ITEM_FAVORITE_APPEARANCES, stmt) && ok; + return ok; } }; @@ -1142,6 +1152,7 @@ void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQue LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS)); _collectionMgr->LoadAccountToys(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_TOYS)); _collectionMgr->LoadAccountHeirlooms(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_HEIRLOOMS)); + _collectionMgr->LoadAccountItemAppearances(holder->GetPreparedResult(AccountInfoQueryHolder::ITEM_APPEARANCES), holder->GetPreparedResult(AccountInfoQueryHolder::ITEM_FAVORITE_APPEARANCES)); if (!m_inQueue) SendAuthResponse(ERROR_OK, false); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 5f155420ff4..02ba3d182ec 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -366,7 +366,6 @@ namespace WorldPackets class SwapItem; class WrapItem; class CancelTempEnchantment; - class TransmogrifyItems; class UseCritterItem; class SocketGems; struct ItemInstance; @@ -666,6 +665,12 @@ namespace WorldPackets class TradeStatus; } + namespace Transmogrification + { + class TransmogrifyItems; + class TransmogAppearanceSetFavorite; + } + namespace Vehicle { class MoveDismissVehicle; @@ -1642,7 +1647,8 @@ class TC_GAME_API WorldSession void SendVoidStorageTransferResult(VoidTransferError result); // Transmogrification - void HandleTransmogrifyItems(WorldPackets::Item::TransmogrifyItems& transmogrifyItems); + void HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems); + void HandleTransmogAppearanceSetFavorite(WorldPackets::Transmogrification::TransmogAppearanceSetFavorite& transmogAppearanceSetFavorite); // Miscellaneous void HandleSpellClick(WorldPackets::Spells::SpellClick& spellClick); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index b7239b21a7b..c69c2ebea5f 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -4137,7 +4137,7 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) { ammoInventoryType = pItem->GetTemplate()->GetInventoryType(); if (ammoInventoryType == INVTYPE_THROWN) - ammoDisplayID = pItem->GetDisplayId(); + ammoDisplayID = pItem->GetDisplayId(m_caster->ToPlayer()); else if (m_caster->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 0cca7a27d2c..3720c2addc8 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5803,9 +5803,9 @@ void Spell::EffectApplyEnchantIllusion(SpellEffIndex /*effIndex*/) return; itemTarget->SetState(ITEM_CHANGED, player); - itemTarget->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION, effectInfo->MiscValue); + itemTarget->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, effectInfo->MiscValue); if (itemTarget->IsEquipped()) - player->SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (itemTarget->GetSlot() * 2), 1, itemTarget->GetVisibleItemVisual()); + player->SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (itemTarget->GetSlot() * 2), 1, itemTarget->GetVisibleItemVisual(player)); player->RemoveTradeableItem(itemTarget); itemTarget->ClearSoulboundTradeable(player); |