TrinityCore
TransmogrificationHandler.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "WorldSession.h"
19#include "CollectionMgr.h"
20#include "DB2Stores.h"
21#include "Item.h"
22#include "Log.h"
23#include "NPCPackets.h"
24#include "ObjectMgr.h"
25#include "Player.h"
27
29{
30 Player* player = GetPlayer();
31 // Validate
33 {
34 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {} not found or player can't interact with it.", transmogrifyItems.Npc.ToString());
35 return;
36 }
37
38 int64 cost = 0;
39 std::unordered_map<Item*, std::pair<uint32, uint32>> transmogItems;
40 std::unordered_map<Item*, uint32> illusionItems;
41
42 std::vector<Item*> resetAppearanceItems;
43 std::vector<Item*> resetIllusionItems;
44 std::vector<uint32> bindAppearances;
45
46 auto validateAndStoreTransmogItem = [&](Item* itemTransmogrified, uint32 itemModifiedAppearanceId, bool isSecondary)
47 {
48 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId);
49 if (!itemModifiedAppearance)
50 {
51 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using invalid appearance ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
52 return false;
53 }
54
55 if (isSecondary && itemTransmogrified->GetTemplate()->GetInventoryType() != INVTYPE_SHOULDERS)
56 {
57 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify secondary appearance to non-shoulder item.", player->GetGUID().ToString(), player->GetName());
58 return false;
59 }
60
61 auto [hasAppearance, isTemporary] = GetCollectionMgr()->HasItemAppearance(itemModifiedAppearanceId);
62 if (!hasAppearance)
63 {
64 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using appearance he has not collected ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
65 return false;
66 }
67 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID);
68 if (player->CanUseItem(itemTemplate) != EQUIP_ERR_OK)
69 {
70 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using appearance he can never use ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
71 return false;
72 }
73
74 // validity of the transmogrification items
75 if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemModifiedAppearance))
76 {
77 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} failed CanTransmogrifyItemWithItem ({} with appearance {}).", player->GetGUID().ToString(), player->GetName(), itemTransmogrified->GetEntry(), itemModifiedAppearanceId);
78 return false;
79 }
80
81 if (!isSecondary)
82 transmogItems[itemTransmogrified].first = itemModifiedAppearanceId;
83 else
84 transmogItems[itemTransmogrified].second = itemModifiedAppearanceId;
85
86 if (isTemporary)
87 bindAppearances.push_back(itemModifiedAppearanceId);
88
89 return true;
90 };
91
92 for (WorldPackets::Transmogrification::TransmogrifyItem const& transmogItem : transmogrifyItems.Items)
93 {
94 // slot of the transmogrified item
95 if (transmogItem.Slot >= EQUIPMENT_SLOT_END)
96 {
97 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player ({}, name: {}) tried to transmogrify wrong slot ({}) when transmogrifying items.", player->GetGUID().ToString(), player->GetName(), transmogItem.Slot);
98 return;
99 }
100
101 // transmogrified item
102 Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot);
103 if (!itemTransmogrified)
104 {
105 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player ({}, name: {}) tried to transmogrify an invalid item in a valid slot (slot: {}).", player->GetGUID().ToString(), player->GetName(), transmogItem.Slot);
106 return;
107 }
108
109 if (transmogItem.ItemModifiedAppearanceID || transmogItem.SecondaryItemModifiedAppearanceID > 0)
110 {
111 if (transmogItem.ItemModifiedAppearanceID && !validateAndStoreTransmogItem(itemTransmogrified, transmogItem.ItemModifiedAppearanceID, false))
112 return;
113
114 if (transmogItem.SecondaryItemModifiedAppearanceID > 0 && !validateAndStoreTransmogItem(itemTransmogrified, transmogItem.SecondaryItemModifiedAppearanceID, true))
115 return;
116
117 // add cost
118 cost += itemTransmogrified->GetSellPrice(_player);
119 }
120 else
121 resetAppearanceItems.push_back(itemTransmogrified);
122
123 if (transmogItem.SpellItemEnchantmentID)
124 {
125 if (transmogItem.Slot != EQUIPMENT_SLOT_MAINHAND && transmogItem.Slot != EQUIPMENT_SLOT_OFFHAND)
126 {
127 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion into non-weapon slot ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.Slot);
128 return;
129 }
130
131 TransmogIllusionEntry const* illusion = sDB2Manager.GetTransmogIllusionForEnchantment(transmogItem.SpellItemEnchantmentID);
132 if (!illusion)
133 {
134 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion using invalid enchant ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.SpellItemEnchantmentID);
135 return;
136 }
137
138 if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(illusion->UnlockConditionID))
139 {
140 if (!ConditionMgr::IsPlayerMeetingCondition(player, condition))
141 {
142 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion using not allowed enchant ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.SpellItemEnchantmentID);
143 return;
144 }
145 }
146
147 illusionItems[itemTransmogrified] = transmogItem.SpellItemEnchantmentID;
148 cost += illusion->TransmogCost;
149 }
150 else
151 resetIllusionItems.push_back(itemTransmogrified);
152 }
153
154 if (!player->HasAuraType(SPELL_AURA_REMOVE_TRANSMOG_COST) && cost) // 0 cost if reverting look
155 {
156 if (!player->HasEnoughMoney(cost))
157 return;
158 player->ModifyMoney(-cost);
159 }
160
161 // Everything is fine, proceed
162 for (auto& transmogPair : transmogItems)
163 {
164 Item* transmogrified = transmogPair.first;
165
166 if (!transmogrifyItems.CurrentSpecOnly)
167 {
168 transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, transmogPair.second.first);
173
174 transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, transmogPair.second.second);
179 }
180 else
181 {
190
199
200 transmogrified->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second.first);
201 transmogrified->SetModifier(SecondaryAppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second.second);
202 }
203
204 player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
205
206 transmogrified->SetNotRefundable(player);
207 transmogrified->ClearSoulboundTradeable(player);
208 transmogrified->SetState(ITEM_CHANGED, player);
209 }
210
211 for (auto& illusionPair : illusionItems)
212 {
213 Item* transmogrified = illusionPair.first;
214
215 if (!transmogrifyItems.CurrentSpecOnly)
216 {
217 transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, illusionPair.second);
222 }
223 else
224 {
233 transmogrified->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], illusionPair.second);
234 }
235
236 player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
237
238 transmogrified->SetNotRefundable(player);
239 transmogrified->ClearSoulboundTradeable(player);
240 transmogrified->SetState(ITEM_CHANGED, player);
241 }
242
243 for (Item* item : resetAppearanceItems)
244 {
245 if (!transmogrifyItems.CurrentSpecOnly)
246 {
248 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0);
249 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0);
250 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0);
251 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0);
252
258 }
259 else
260 {
261 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1))
263 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2))
265 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3))
267 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4))
269
278
279 item->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
280 item->SetModifier(SecondaryAppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
281 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
282 }
283
284 item->SetState(ITEM_CHANGED, player);
285 player->SetVisibleItemSlot(item->GetSlot(), item);
286 }
287
288 for (Item* item : resetIllusionItems)
289 {
290 if (!transmogrifyItems.CurrentSpecOnly)
291 {
292 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
293 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0);
294 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0);
295 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0);
296 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0);
297 }
298 else
299 {
300 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1))
302 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2))
304 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3))
306 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4))
308
309 item->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
311 }
312
313 item->SetState(ITEM_CHANGED, player);
314 player->SetVisibleItemSlot(item->GetSlot(), item);
315 }
316
317 for (uint32 itemModifedAppearanceId : bindAppearances)
318 {
319 std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId);
320 for (ObjectGuid const& itemGuid : itemsProvidingAppearance)
321 {
322 if (Item* item = player->GetItemByGuid(itemGuid))
323 {
324 item->SetNotRefundable(player);
325 item->ClearSoulboundTradeable(player);
327 }
328 }
329 }
330}
331
333{
335 npcInteraction.Npc = guid;
337 npcInteraction.Success = true;
338 SendPacket(npcInteraction.Write());
339}
DB2Storage< ItemModifiedAppearanceEntry > sItemModifiedAppearanceStore("ItemModifiedAppearance.db2", &ItemModifiedAppearanceLoadInfo::Instance)
DB2Storage< PlayerConditionEntry > sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:538
int64_t int64
Definition: Define.h:137
uint32_t uint32
Definition: Define.h:142
@ EQUIP_ERR_OK
Definition: ItemDefines.h:26
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3
Definition: ItemDefines.h:245
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2
Definition: ItemDefines.h:223
@ ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS
Definition: ItemDefines.h:218
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS
Definition: ItemDefines.h:242
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4
Definition: ItemDefines.h:246
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1
Definition: ItemDefines.h:221
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4
Definition: ItemDefines.h:226
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4
Definition: ItemDefines.h:227
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1
Definition: ItemDefines.h:243
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3
Definition: ItemDefines.h:225
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2
Definition: ItemDefines.h:222
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3
Definition: ItemDefines.h:224
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS
Definition: ItemDefines.h:211
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2
Definition: ItemDefines.h:244
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1
Definition: ItemDefines.h:212
@ INVTYPE_SHOULDERS
Definition: ItemTemplate.h:382
ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition: Item.cpp:283
ItemModifier const SecondaryAppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition: Item.cpp:301
ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition: Item.cpp:292
@ ITEM_CHANGED
Definition: Item.h:55
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define sObjectMgr
Definition: ObjectMgr.h:1946
@ EQUIPMENT_SLOT_MAINHAND
Definition: Player.h:646
@ EQUIPMENT_SLOT_END
Definition: Player.h:650
@ EQUIPMENT_SLOT_OFFHAND
Definition: Player.h:647
#define INVENTORY_SLOT_BAG_0
Definition: Player.h:625
@ SPELL_AURA_REMOVE_TRANSMOG_COST
@ UNIT_NPC_FLAG_TRANSMOGRIFIER
Definition: UnitDefines.h:325
@ UNIT_NPC_FLAG_2_NONE
Definition: UnitDefines.h:336
void AddItemAppearance(Item *item)
std::unordered_set< ObjectGuid > GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const
std::pair< bool, bool > HasItemAppearance(uint32 itemModifiedAppearanceId) const
static bool IsPlayerMeetingCondition(Player const *player, PlayerConditionEntry const *condition)
Definition: Item.h:170
void SetState(ItemUpdateState state, Player *forplayer=nullptr)
Definition: Item.cpp:1166
uint8 GetSlot() const
Definition: Item.h:280
static bool CanTransmogrifyItemWithItem(Item const *item, ItemModifiedAppearanceEntry const *itemModifiedAppearance)
Definition: Item.cpp:2067
void SetModifier(ItemModifier modifier, uint32 value)
Definition: Item.cpp:2436
ItemTemplate const * GetTemplate() const
Definition: Item.cpp:1141
void SetNotRefundable(Player *owner, bool changestate=true, CharacterDatabaseTransaction *trans=nullptr, bool addToCollection=true)
Definition: Item.cpp:1843
uint32 GetSellPrice(Player const *owner) const
Definition: Item.cpp:2248
void ClearSoulboundTradeable(Player *currentOwner)
Definition: Item.cpp:1913
uint32 GetModifier(ItemModifier modifier) const
Definition: Item.cpp:2423
std::string ToString() const
Definition: ObjectGuid.cpp:554
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
bool ModifyMoney(int64 amount, bool sendError=true)
Definition: Player.cpp:24098
Creature * GetNPCIfCanInteractWith(ObjectGuid const &guid, NPCFlags npcFlags, NPCFlags2 npcFlags2) const
Definition: Player.cpp:1947
Item * GetItemByPos(uint16 pos) const
Definition: Player.cpp:9582
uint8 GetActiveTalentGroup() const
Definition: Player.h:1843
void SetVisibleItemSlot(uint8 slot, Item *pItem)
Definition: Player.cpp:11995
Item * GetItemByGuid(ObjectGuid guid) const
Definition: Player.cpp:9566
bool HasEnoughMoney(uint64 amount) const
Definition: Player.h:1740
InventoryResult CanUseItem(Item *pItem, bool not_loading=true) const
Definition: Player.cpp:11381
bool HasAuraType(AuraType auraType) const
Definition: Unit.cpp:4674
std::string const & GetName() const
Definition: Object.h:555
WorldPacket const * Write() override
Definition: NPCPackets.cpp:94
Array< TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS > Items
void HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems &transmogrifyItems)
Player * GetPlayer() const
void SendOpenTransmogrifier(ObjectGuid const &guid)
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
Player * _player
CollectionMgr * GetCollectionMgr() const
InventoryType GetInventoryType() const
Definition: ItemTemplate.h:786