TrinityCore
Loading...
Searching...
No Matches
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"
26#include "TransmogMgr.h"
28
30{
31 Player* player = GetPlayer();
32 // Validate
34 {
35 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {} not found or player can't interact with it.", transmogrifyItems.Npc.ToString());
36 return;
37 }
38
39 int64 cost = 0;
40 std::unordered_map<Item*, std::pair<uint32, uint32>> transmogItems;
41 std::unordered_map<Item*, uint32> illusionItems;
42
43 std::vector<Item*> resetAppearanceItems;
44 std::vector<Item*> resetIllusionItems;
45 std::vector<uint32> bindAppearances;
46
47 auto validateAndStoreTransmogItem = [&](Item* itemTransmogrified, uint32 itemModifiedAppearanceId, bool isSecondary)
48 {
49 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId);
50 if (!itemModifiedAppearance)
51 {
52 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using invalid appearance ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
53 return false;
54 }
55
56 if (isSecondary && itemTransmogrified->GetTemplate()->GetInventoryType() != INVTYPE_SHOULDERS)
57 {
58 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify secondary appearance to non-shoulder item.", player->GetGUID().ToString(), player->GetName());
59 return false;
60 }
61
62 auto [hasAppearance, isTemporary] = GetCollectionMgr()->HasItemAppearance(itemModifiedAppearanceId);
63 if (!hasAppearance)
64 {
65 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using appearance he has not collected ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
66 return false;
67 }
68 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID);
69 if (player->CanUseItem(itemTemplate) != EQUIP_ERR_OK)
70 {
71 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify using appearance he can never use ({}).", player->GetGUID().ToString(), player->GetName(), itemModifiedAppearanceId);
72 return false;
73 }
74
75 // validity of the transmogrification items
76 if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemModifiedAppearance))
77 {
78 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} failed CanTransmogrifyItemWithItem ({} with appearance {}).", player->GetGUID().ToString(), player->GetName(), itemTransmogrified->GetEntry(), itemModifiedAppearanceId);
79 return false;
80 }
81
82 if (!isSecondary)
83 transmogItems[itemTransmogrified].first = itemModifiedAppearanceId;
84 else
85 transmogItems[itemTransmogrified].second = itemModifiedAppearanceId;
86
87 if (isTemporary)
88 bindAppearances.push_back(itemModifiedAppearanceId);
89
90 return true;
91 };
92
93 for (WorldPackets::Transmogrification::TransmogrifyItem const& transmogItem : transmogrifyItems.Items)
94 {
95 // slot of the transmogrified item
96 if (transmogItem.Slot >= EQUIPMENT_SLOT_END)
97 {
98 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player ({}, name: {}) tried to transmogrify wrong slot ({}) when transmogrifying items.", player->GetGUID().ToString(), player->GetName(), transmogItem.Slot);
99 return;
100 }
101
102 // transmogrified item
103 Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot);
104 if (!itemTransmogrified)
105 {
106 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);
107 return;
108 }
109
110 if (transmogItem.ItemModifiedAppearanceID || transmogItem.SecondaryItemModifiedAppearanceID > 0)
111 {
112 if (transmogItem.ItemModifiedAppearanceID && !validateAndStoreTransmogItem(itemTransmogrified, transmogItem.ItemModifiedAppearanceID, false))
113 return;
114
115 if (transmogItem.SecondaryItemModifiedAppearanceID > 0 && !validateAndStoreTransmogItem(itemTransmogrified, transmogItem.SecondaryItemModifiedAppearanceID, true))
116 return;
117
118 // add cost
119 cost += itemTransmogrified->GetSellPrice(_player);
120 }
121 else
122 resetAppearanceItems.push_back(itemTransmogrified);
123
124 if (transmogItem.SpellItemEnchantmentID)
125 {
126 if (transmogItem.Slot != EQUIPMENT_SLOT_MAINHAND && transmogItem.Slot != EQUIPMENT_SLOT_OFFHAND)
127 {
128 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion into non-weapon slot ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.Slot);
129 return;
130 }
131
132 TransmogIllusionEntry const* illusion = TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(transmogItem.SpellItemEnchantmentID);
133 if (!illusion)
134 {
135 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion using invalid enchant ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.SpellItemEnchantmentID);
136 return;
137 }
138
140 {
141 TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion using not allowed enchant ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.SpellItemEnchantmentID);
142 return;
143 }
144
145 illusionItems[itemTransmogrified] = transmogItem.SpellItemEnchantmentID;
146 cost += illusion->TransmogCost;
147 }
148 else
149 resetIllusionItems.push_back(itemTransmogrified);
150 }
151
152 if (!player->HasAuraType(SPELL_AURA_REMOVE_TRANSMOG_COST) && cost) // 0 cost if reverting look
153 {
154 if (!player->HasEnoughMoney(cost))
155 return;
156 player->ModifyMoney(-cost);
157 }
158
159 // Everything is fine, proceed
160 for (auto& transmogPair : transmogItems)
161 {
162 Item* transmogrified = transmogPair.first;
163
164 if (!transmogrifyItems.CurrentSpecOnly)
165 {
166 transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, transmogPair.second.first);
171
172 transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS, transmogPair.second.second);
177 }
178 else
179 {
188
197
198 transmogrified->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second.first);
199 transmogrified->SetModifier(SecondaryAppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second.second);
200 }
201
202 player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
203
204 transmogrified->SetNotRefundable(player);
205 transmogrified->ClearSoulboundTradeable(player);
206 transmogrified->SetState(ITEM_CHANGED, player);
207 }
208
209 for (auto& illusionPair : illusionItems)
210 {
211 Item* transmogrified = illusionPair.first;
212
213 if (!transmogrifyItems.CurrentSpecOnly)
214 {
215 transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, illusionPair.second);
220 }
221 else
222 {
231 transmogrified->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], illusionPair.second);
232 }
233
234 player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
235
236 transmogrified->SetNotRefundable(player);
237 transmogrified->ClearSoulboundTradeable(player);
238 transmogrified->SetState(ITEM_CHANGED, player);
239 }
240
241 for (Item* item : resetAppearanceItems)
242 {
243 if (!transmogrifyItems.CurrentSpecOnly)
244 {
246 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0);
247 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0);
248 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0);
249 item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0);
250
256 }
257 else
258 {
259 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1))
261 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2))
263 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3))
265 if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4))
267
276
277 item->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
278 item->SetModifier(SecondaryAppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
279 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
280 }
281
282 item->SetState(ITEM_CHANGED, player);
283 player->SetVisibleItemSlot(item->GetSlot(), item);
284 }
285
286 for (Item* item : resetIllusionItems)
287 {
288 if (!transmogrifyItems.CurrentSpecOnly)
289 {
290 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
291 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0);
292 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0);
293 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0);
294 item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0);
295 }
296 else
297 {
298 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1))
300 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2))
302 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3))
304 if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4))
306
307 item->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
309 }
310
311 item->SetState(ITEM_CHANGED, player);
312 player->SetVisibleItemSlot(item->GetSlot(), item);
313 }
314
315 for (uint32 itemModifedAppearanceId : bindAppearances)
316 {
317 std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId);
318 for (ObjectGuid const& itemGuid : itemsProvidingAppearance)
319 {
320 if (Item* item = player->GetItemByGuid(itemGuid))
321 {
322 item->SetNotRefundable(player);
323 item->ClearSoulboundTradeable(player);
325 }
326 }
327 }
328}
329
331{
333 {
334 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
335 GetPlayerInfo(), transmogOutfitNew.Npc);
336 return;
337 }
338
340
341 if (transmogOutfitNew.Source != TransmogOutfitEntrySource::PlayerPurchased)
342 {
343 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - source {} not allowed.",
344 GetPlayerInfo(), transmogOutfitNew.Source);
345 return;
346 }
347
348 if (transmogOutfitNew.Info.SetType != TransmogOutfitSetType::Outfit)
349 {
350 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - set type {} not allowed.",
351 GetPlayerInfo(), transmogOutfitNew.Info.SetType);
352 return;
353 }
354
355 TransmogOutfitEntryEntry const* transmogOutfitEntry = TransmogMgr::GetNextOutfitToUnlock(transmogOutfitNew.Source, _player);
356 if (!transmogOutfitEntry)
357 {
358 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - no next unlockable outfit entry found for source {}.",
359 GetPlayerInfo(), transmogOutfitNew.Source);
360 return;
361 }
362
363 if (!_player->HasEnoughMoney(transmogOutfitEntry->Cost))
364 {
365 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - not enough money.",
366 GetPlayerInfo());
367 return;
368 }
369
370 GetCollectionMgr()->AddTransmogOutfit(transmogOutfitEntry->ID);
371 _player->CreateTransmogOutfit(transmogOutfitEntry->ID, transmogOutfitNew.Info);
372 _player->ModifyMoney(-int64(transmogOutfitEntry->Cost));
373
375 transmogOutfitNewEntryAdded.TransmogOutfitID = transmogOutfitEntry->ID;
376 SendPacket(transmogOutfitNewEntryAdded.Write());
377}
378
380{
382 {
383 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
384 GetPlayerInfo(), transmogOutfitUpdateInfo.Npc);
385 return;
386 }
387
389
390 if (!_player->UpdateTransmogOutfit(transmogOutfitUpdateInfo.OutfitID, transmogOutfitUpdateInfo.Info))
391 {
392 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateInfo - player does not have outfit {}.",
393 GetPlayerInfo(), transmogOutfitUpdateInfo.OutfitID);
394 return;
395 }
396
397 // SMSG_UPDATE_OBJECT must be received by client before transmog packet for UI to properly update
400 sendUpdateObject(_player);
401
403 transmogOutfitInfoUpdated.TransmogOutfitID = transmogOutfitUpdateInfo.OutfitID;
404 transmogOutfitInfoUpdated.OutfitInfo = &transmogOutfitUpdateInfo.Info;
405 SendPacket(transmogOutfitInfoUpdated.Write());
406}
407
409{
411 {
412 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
413 GetPlayerInfo(), transmogOutfitUpdateSituations.Npc);
414 return;
415 }
416
418
419 if (!_player->m_activePlayerData->TransmogOutfits.Get(transmogOutfitUpdateSituations.OutfitID))
420 {
421 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSituations - player does not have outfit {}.",
422 GetPlayerInfo(), transmogOutfitUpdateSituations.OutfitID);
423 return;
424 }
425
426 if (!TransmogMgr::ValidateSituations(transmogOutfitUpdateSituations.Situations))
427 {
428 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSituations - player sent invalid situations.",
429 GetPlayerInfo());
430 return;
431 }
432
433 _player->UpdateTransmogOutfitSituations(transmogOutfitUpdateSituations.OutfitID, transmogOutfitUpdateSituations.SituationsEnabled,
434 transmogOutfitUpdateSituations.Situations);
435
436 // SMSG_UPDATE_OBJECT must be received by client before transmog packet for UI to properly update
439 sendUpdateObject(_player);
440
442 transmogOutfitSituationsUpdated.TransmogOutfitID = transmogOutfitUpdateSituations.OutfitID;
443 transmogOutfitSituationsUpdated.SituationsEnabled = transmogOutfitUpdateSituations.SituationsEnabled;
444 transmogOutfitSituationsUpdated.Situations = transmogOutfitUpdateSituations.Situations;
445 SendPacket(transmogOutfitSituationsUpdated.Write());
446}
447
449{
451 {
452 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
453 GetPlayerInfo(), transmogOutfitUpdateSlots.Npc);
454 return;
455 }
456
458
459 UF::TransmogOutfitData const* transmogOutfit = _player->m_activePlayerData->TransmogOutfits.Get(transmogOutfitUpdateSlots.OutfitID);
460 if (!transmogOutfit)
461 {
462 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have outfit {}.",
463 GetPlayerInfo(), transmogOutfitUpdateSlots.OutfitID);
464 return;
465 }
466
467 if (!TransmogMgr::ValidateSlots(transmogOutfitUpdateSlots.Slots))
468 {
469 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player sent invalid slots.",
470 GetPlayerInfo());
471 return;
472 }
473
474 std::vector<uint32> bindAppearances;
475
476 for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : transmogOutfitUpdateSlots.Slots)
477 {
478 if (slot.ItemModifiedAppearanceID)
479 {
480 auto [hasAppearance, isTemporary] = GetCollectionMgr()->HasItemAppearance(slot.ItemModifiedAppearanceID);
481 if (!hasAppearance)
482 {
483 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have appearance {} in collection.",
484 GetPlayerInfo(), slot.ItemModifiedAppearanceID);
485 return;
486 }
487
488 if (isTemporary)
489 bindAppearances.push_back(slot.ItemModifiedAppearanceID);
490 }
491
492 if (slot.SpellItemEnchantmentID && !GetCollectionMgr()->HasTransmogIllusion(TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(slot.SpellItemEnchantmentID)->ID))
493 {
494 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have enchant {} in illusion collection.",
495 GetPlayerInfo(), slot.SpellItemEnchantmentID);
496 return;
497 }
498 }
499
501 {
502 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player has already claimed free transmog before.",
503 GetPlayerInfo());
504 return;
505 }
506
507 // calculate cost
508 float baseCost = 0;
509 if (uint32 curveId = sDB2Manager.GetGlobalCurveId(GlobalCurve::TransmogCost))
510 baseCost = sDB2Manager.GetCurveValueAt(curveId, std::max<int32>(_player->GetLevel(), _player->m_activePlayerData->TransmogCostMinScalingLevel));
511
512 float costMultiplier = 1.0f;
513 TransmogOutfitEntryEntry const* transmogOutfitEntry = sTransmogOutfitEntryStore.AssertEntry(transmogOutfitUpdateSlots.OutfitID);
515 costMultiplier *= transmogOutfitEntry->OverrideCostModifier;
516
518 costMultiplier *= _player->m_activePlayerData->TransmogMetadata->CostMod;
519
520 if (sChrRacesStore.AssertEntry(_player->GetRace())->GetFlags().HasFlag(ChrRacesFlag::VoidVendorDiscount))
521 costMultiplier *= 0.5f;
522
523 uint64 cost = 0;
524
525 if (!transmogOutfitUpdateSlots.UseAvailableDiscount)
526 {
527 auto oldSlotItr = transmogOutfit->Slots.begin();
528 auto oldSlotEnd = transmogOutfit->Slots.end();
529 for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : transmogOutfitUpdateSlots.Slots)
530 {
531 oldSlotItr = std::ranges::find(oldSlotItr, oldSlotEnd,
532 std::pair(AsUnderlyingType(slot.Slot), AsUnderlyingType(slot.SlotOption)),
533 [](UF::TransmogOutfitSlotData const& slotData) { return std::pair(*slotData.Slot, *slotData.SlotOption); });
534
535 auto [slotEntry, slotOptionEntry, _] = *TransmogMgr::GetSlotAndOption(slot.Slot, slot.SlotOption);
536
537 if (slot.AppearanceDisplayType == TransmogOutfitDisplayType::Assigned && oldSlotItr->ItemModifiedAppearanceID != slot.ItemModifiedAppearanceID)
538 {
539 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(slot.ItemModifiedAppearanceID);
540 if (!itemModifiedAppearance || !sTransmogHolidayStore.HasRecord(itemModifiedAppearance->ItemID))
541 {
542 if (slotEntry)
543 cost = static_cast<uint64>(std::floor(baseCost * slotEntry->ItemCostMultiplier)) + cost;
544
545 if (slotOptionEntry)
546 cost = static_cast<uint64>(std::floor(baseCost * slotOptionEntry->ItemCostMultiplier)) + cost;
547 }
548 }
549
550 if (slot.IllusionDisplayType == TransmogOutfitDisplayType::Assigned && oldSlotItr->SpellItemEnchantmentID != slot.SpellItemEnchantmentID)
551 {
552 if (slotEntry)
553 cost = static_cast<uint64>(std::floor(baseCost * slotEntry->IllusionCostMultiplier)) + cost;
554
555 if (slotOptionEntry)
556 cost = static_cast<uint64>(std::floor(baseCost * slotOptionEntry->IllusionCostMultiplier)) + cost;
557 }
558
559 ++oldSlotItr;
560 }
561
562 cost = static_cast<uint64>(std::clamp(costMultiplier, 0.0f, 1.0f) * cost);
563
564 if (cost != transmogOutfitUpdateSlots.Cost)
565 {
566 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player sent invalid cost {}.",
567 GetPlayerInfo(), transmogOutfitUpdateSlots.Cost);
568 return;
569 }
570
571 if (!_player->HasEnoughMoney(cost))
572 {
573 TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - not enough money.",
574 GetPlayerInfo());
575 return;
576 }
577 }
578 else
579 {
582 }
583
584 _player->ModifyMoney(-int64(cost));
585
586 _player->UpdateTransmogOutfitSlots(transmogOutfitUpdateSlots.OutfitID, transmogOutfitUpdateSlots.Slots);
587
588 if (transmogOutfitUpdateSlots.OutfitID == _player->m_activePlayerData->TransmogMetadata->TransmogOutfitID)
590
592 transmogOutfitSlotsUpdated.TransmogOutfitID = transmogOutfitUpdateSlots.OutfitID;
593 transmogOutfitSlotsUpdated.Slots = transmogOutfitUpdateSlots.Slots;
594 SendPacket(transmogOutfitSlotsUpdated.Write());
595
596 for (uint32 itemModifedAppearanceId : bindAppearances)
597 {
598 std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId);
599 for (ObjectGuid const& itemGuid : itemsProvidingAppearance)
600 {
601 if (Item* item = _player->GetItemByGuid(itemGuid))
602 {
603 item->SetNotRefundable(_player);
604 item->ClearSoulboundTradeable(_player);
606 }
607 }
608 }
609}
610
612{
614 npcInteraction.Npc = guid;
616 npcInteraction.Success = true;
617 SendPacket(npcInteraction.Write());
618}
DB2Storage< TransmogOutfitEntryEntry > sTransmogOutfitEntryStore("TransmogOutfitEntry.db2", &TransmogOutfitEntryLoadInfo::Instance)
DB2Storage< ItemModifiedAppearanceEntry > sItemModifiedAppearanceStore("ItemModifiedAppearance.db2", &ItemModifiedAppearanceLoadInfo::Instance)
DB2Storage< ChrRacesEntry > sChrRacesStore("ChrRaces.db2", &ChrRacesLoadInfo::Instance)
DB2Storage< TransmogHolidayEntry > sTransmogHolidayStore("TransmogHoliday.db2", &TransmogHolidayLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
int64_t int64
Definition Define.h:149
uint64_t uint64
Definition Define.h:153
uint32_t uint32
Definition Define.h:154
@ EQUIP_ERR_OK
Definition ItemDefines.h:26
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_3
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2
@ ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_ALL_SPECS
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_4
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_1
@ ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS
@ ITEM_MODIFIER_TRANSMOG_SECONDARY_APPEARANCE_SPEC_2
@ ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1
@ INVTYPE_SHOULDERS
ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition Item.cpp:325
ItemModifier const SecondaryAppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition Item.cpp:343
ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS]
Definition Item.cpp:334
@ ITEM_CHANGED
Definition Item.h:47
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define sObjectMgr
Definition ObjectMgr.h:1885
@ EQUIPMENT_SLOT_MAINHAND
Definition Player.h:744
@ EQUIPMENT_SLOT_END
Definition Player.h:748
@ EQUIPMENT_SLOT_OFFHAND
Definition Player.h:745
@ PLAYER_LOCAL_FLAG_FREE_TRANSMOG_CLAIMED
Definition Player.h:591
#define INVENTORY_SLOT_BAG_0
Definition Player.h:723
@ SPELL_AURA_MOD_TRANSMOG_OUTFIT_UPDATE_COST
@ SPELL_AURA_REMOVE_TRANSMOG_COST
@ UNIT_NPC_FLAG_TRANSMOGRIFIER
@ UNIT_NPC_FLAG_2_NONE
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
void AddItemAppearance(Item *item)
void AddTransmogOutfit(int32 transmogOutfitId)
std::unordered_set< ObjectGuid > GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const
std::pair< bool, bool > HasItemAppearance(uint32 itemModifiedAppearanceId) const
static bool IsPlayerMeetingCondition(Player const *player, uint32 conditionId)
Definition Item.h:179
void SetState(ItemUpdateState state, Player *forplayer=nullptr)
Definition Item.cpp:1258
uint8 GetSlot() const
Definition Item.h:290
static bool CanTransmogrifyItemWithItem(Item const *item, ItemModifiedAppearanceEntry const *itemModifiedAppearance)
Definition Item.cpp:2053
void SetModifier(ItemModifier modifier, uint32 value)
Definition Item.cpp:2483
ItemTemplate const * GetTemplate() const
Definition Item.cpp:1233
void SetNotRefundable(Player *owner, bool changestate=true, CharacterDatabaseTransaction *trans=nullptr, bool addToCollection=true)
Definition Item.cpp:1929
uint32 GetSellPrice(Player const *owner) const
Definition Item.cpp:2234
void ClearSoulboundTradeable(Player *currentOwner)
Definition Item.cpp:1969
uint32 GetModifier(ItemModifier modifier) const
Definition Item.cpp:2470
std::string ToString() const
uint32 GetEntry() const
Definition Object.h:89
bool UpdateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const &outfitData)
Definition Player.cpp:31183
bool ModifyMoney(int64 amount, bool sendError=true)
Definition Player.cpp:24850
void SetPlayerLocalFlag(PlayerLocalFlags flags)
Definition Player.h:3008
void SetVisibleItemSlot(uint8 slot, Item const *item)
Definition Player.cpp:11830
Creature * GetNPCIfCanInteractWith(ObjectGuid const &guid, NPCFlags npcFlags, NPCFlags2 npcFlags2) const
Definition Player.cpp:1903
UF::UpdateField< UF::ActivePlayerData, int32(WowCS::EntityFragment::CGObject), TYPEID_ACTIVE_PLAYER > m_activePlayerData
Definition Player.h:3062
Item * GetItemByPos(uint16 pos) const
Definition Player.cpp:9630
void EquipTransmogOutfit(uint32 id, TransmogSituationTrigger trigger, Optional< bool > locked)
Definition Player.cpp:31251
bool HasPlayerLocalFlag(PlayerLocalFlags flags) const
Definition Player.h:3007
void UpdateTransmogOutfitSlots(uint32 id, std::span< WorldPackets::Transmogrification::TransmogOutfitSlotData const > slots)
Definition Player.cpp:31221
uint8 GetActiveTalentGroup() const
Definition Player.h:2010
void UpdateTransmogOutfitSituations(uint32 id, bool situationsEnabled, std::span< WorldPackets::Transmogrification::TransmogOutfitSituationInfo const > situations)
Definition Player.cpp:31200
Item * GetItemByGuid(ObjectGuid guid) const
Definition Player.cpp:9614
void CreateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const &outfitData)
Definition Player.cpp:31131
bool HasEnoughMoney(uint64 amount) const
Definition Player.h:1907
void SetHasClaimedFreeTransmog()
Definition Player.h:1327
InventoryResult CanUseItem(Item *pItem, bool not_loading=true) const
Definition Player.cpp:11214
std::vector< T >::const_iterator begin() const
std::vector< T >::const_iterator end() const
void MarkChanged(UpdateField< T, BlockBit, Bit >(Derived::*))
bool HasAuraType(AuraType auraType) const
Definition Unit.cpp:4814
void RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const *source=nullptr)
Definition Unit.cpp:4241
uint8 GetLevel() const
Definition Unit.h:757
uint8 GetRace() const
Definition Unit.h:761
std::string const & GetName() const
Definition Object.h:342
WorldPacket const * Write() override
Array< TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS > Items
void HandleTransmogOutfitUpdateSituations(WorldPackets::Transmogrification::TransmogOutfitUpdateSituations const &transmogOutfitUpdateSituations)
void HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems &transmogrifyItems)
void HandleTransmogOutfitNew(WorldPackets::Transmogrification::TransmogOutfitNew const &transmogOutfitNew)
void HandleTransmogOutfitUpdateSlots(WorldPackets::Transmogrification::TransmogOutfitUpdateSlots const &transmogOutfitUpdateSlots)
void HandleTransmogOutfitUpdateInfo(WorldPackets::Transmogrification::TransmogOutfitUpdateInfo const &transmogOutfitUpdateInfo)
std::string GetPlayerInfo() const
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
bool ValidateSituations(std::span< WorldPackets::Transmogrification::TransmogOutfitSituationInfo const > situations)
TransmogIllusionEntry const * GetTransmogIllusionForSpellItemEnchantment(uint32 spellItemEnchantmentId)
bool ValidateSlots(std::span< WorldPackets::Transmogrification::TransmogOutfitSlotData const > slots)
TransmogOutfitSlotAndOptionInfo const * GetSlotAndOption(TransmogOutfitSlot slot, TransmogOutfitSlotOption slotOption)
TransmogOutfitEntryEntry const * GetNextOutfitToUnlock(TransmogOutfitEntrySource source, Player const *player)
InventoryType GetInventoryType() const
UF::ActivePlayerData::Base ActivePlayerMask
Definition Player.h:2295
bool HasFlag(TransmogOutfitEntryFlags flag) const
MapUpdateField< uint32, UF::TransmogOutfitData, 134, 158 > TransmogOutfits
DynamicUpdateField< UF::TransmogOutfitSlotData, -1, 1 > Slots