· 7 years ago · Feb 04, 2019, 07:52 PM
1diff --git a/src/server/game/Entities/Item/TransmogDisplayVendor.cpp b/src/server/game/Entities/Item/TransmogDisplayVendor.cpp
2new file mode 100644
3index 0000000..aa7193b
4--- /dev/null
5+++ b/src/server/game/Entities/Item/TransmogDisplayVendor.cpp
6@@ -0,0 +1,560 @@
7+/*
8+Transmog display vendor
9+Code by Rochet2
10+Ideas LilleCarl
11+
12+ScriptName for NPC:
13+NPC_TransmogDisplayVendor
14+
15+Compatible with Transmogrification 6.1 by Rochet2
16+http://rochet2.github.io/Transmogrification
17+*/
18+
19+#include "TransmogDisplayVendorConf.h"
20+#include "Common.h"
21+#include "QueryResult.h"
22+#include "DatabaseEnv.h"
23+#include "Transaction.h"
24+#include "Field.h"
25+#include "DBCStructure.h"
26+#include "Item.h"
27+#include "ItemPrototype.h"
28+#include "Language.h"
29+#include "Log.h"
30+#include "Player.h"
31+#include "ObjectGuid.h"
32+#include "ObjectMgr.h"
33+#include "WorldSession.h"
34+#include "GameEventMgr.h"
35+#include <sstream>
36+#include <string>
37+
38+// Config start
39+
40+// Edit Transmogrification compatibility in TransmogDisplayVendorConf.h
41+
42+// A multiplier for the default gold cost (change to 0.0f for no default cost)
43+const float TransmogDisplayVendorMgr::ScaledCostModifier = 1.0f;
44+// Cost added on top of other costs (can be negative)
45+const int32 TransmogDisplayVendorMgr::CopperCost = 0;
46+// For custom gold cost set ScaledCostModifier to 0.0f and CopperCost to what ever cost you want
47+
48+const bool TransmogDisplayVendorMgr::RequireToken = false;
49+const uint32 TransmogDisplayVendorMgr::TokenEntry = 49426;
50+const uint32 TransmogDisplayVendorMgr::TokenAmount = 1;
51+
52+const bool TransmogDisplayVendorMgr::AllowPoor = false;
53+const bool TransmogDisplayVendorMgr::AllowCommon = false;
54+const bool TransmogDisplayVendorMgr::AllowUncommon = true;
55+const bool TransmogDisplayVendorMgr::AllowRare = true;
56+const bool TransmogDisplayVendorMgr::AllowEpic = true;
57+const bool TransmogDisplayVendorMgr::AllowLegendary = false;
58+const bool TransmogDisplayVendorMgr::AllowArtifact = false;
59+const bool TransmogDisplayVendorMgr::AllowHeirloom = true;
60+
61+const bool TransmogDisplayVendorMgr::AllowMixedArmorTypes = false;
62+const bool TransmogDisplayVendorMgr::AllowMixedWeaponTypes = false;
63+const bool TransmogDisplayVendorMgr::AllowFishingPoles = false;
64+
65+const bool TransmogDisplayVendorMgr::IgnoreReqRace = false;
66+const bool TransmogDisplayVendorMgr::IgnoreReqClass = false;
67+const bool TransmogDisplayVendorMgr::IgnoreReqSkill = false;
68+const bool TransmogDisplayVendorMgr::IgnoreReqSpell = false;
69+const bool TransmogDisplayVendorMgr::IgnoreReqLevel = false;
70+const bool TransmogDisplayVendorMgr::IgnoreReqEvent = false;
71+const bool TransmogDisplayVendorMgr::IgnoreReqStats = false;
72+
73+// Example AllowedItems[] = { 123, 234, 345 };
74+const std::set<uint32> TransmogDisplayVendorMgr::AllowedItems = { };
75+const std::set<uint32> TransmogDisplayVendorMgr::NotAllowedItems = { };
76+
77+// Config end
78+
79+std::vector<uint32> TransmogDisplayVendorMgr::Allowed;
80+std::vector<uint32> TransmogDisplayVendorMgr::NotAllowed;
81+SelectionStore TransmogDisplayVendorMgr::selectionStore;
82+TransmogDisplayVendorMgr::EntryVector* TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR][MAX_INVTYPE][MAX_ITEM_QUALITY];
83+
84+uint32 TransmogDisplayVendorMgr::GetFakeEntry(const Item* item)
85+{
86+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetFakeEntry");
87+
88+ Player* owner = item->GetOwner();
89+
90+ if (!owner)
91+ return 0;
92+ if (owner->transmogMap.empty())
93+ return 0;
94+
95+ TransmogMapType::const_iterator it = owner->transmogMap.find(item->GetGUID());
96+ if (it == owner->transmogMap.end())
97+ return 0;
98+ return it->second;
99+}
100+void TransmogDisplayVendorMgr::DeleteFakeEntry(Player* player, Item* item)
101+{
102+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::DeleteFakeEntry");
103+
104+ if (player->transmogMap.erase(item->GetGUID()) != 0)
105+ UpdateItem(player, item);
106+}
107+void TransmogDisplayVendorMgr::SetFakeEntry(Player* player, Item* item, uint32 entry)
108+{
109+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SetFakeEntry");
110+
111+ player->transmogMap[item->GetGUID()] = entry;
112+ UpdateItem(player, item);
113+}
114+void TransmogDisplayVendorMgr::UpdateItem(Player* player, Item* item)
115+{
116+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::UpdateItem");
117+
118+ if (item->IsEquipped())
119+ {
120+ player->SetVisibleItemSlot(item->GetSlot(), item);
121+ if (player->IsInWorld())
122+ item->SendUpdateToPlayer(player);
123+ }
124+}
125+const char* TransmogDisplayVendorMgr::getSlotName(uint8 slot, WorldSession* /*session*/)
126+{
127+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::TransmogDisplayVendorMgr::getSlotName");
128+
129+ switch (slot)
130+ {
131+ case EQUIPMENT_SLOT_HEAD: return "Head";// session->GetTrinityString(LANG_SLOT_NAME_HEAD);
132+ case EQUIPMENT_SLOT_SHOULDERS: return "Shoulders";// session->GetTrinityString(LANG_SLOT_NAME_SHOULDERS);
133+ case EQUIPMENT_SLOT_BODY: return "Shirt";// session->GetTrinityString(LANG_SLOT_NAME_BODY);
134+ case EQUIPMENT_SLOT_CHEST: return "Chest";// session->GetTrinityString(LANG_SLOT_NAME_CHEST);
135+ case EQUIPMENT_SLOT_WAIST: return "Waist";// session->GetTrinityString(LANG_SLOT_NAME_WAIST);
136+ case EQUIPMENT_SLOT_LEGS: return "Legs";// session->GetTrinityString(LANG_SLOT_NAME_LEGS);
137+ case EQUIPMENT_SLOT_FEET: return "Feet";// session->GetTrinityString(LANG_SLOT_NAME_FEET);
138+ case EQUIPMENT_SLOT_WRISTS: return "Wrists";// session->GetTrinityString(LANG_SLOT_NAME_WRISTS);
139+ case EQUIPMENT_SLOT_HANDS: return "Hands";// session->GetTrinityString(LANG_SLOT_NAME_HANDS);
140+ case EQUIPMENT_SLOT_BACK: return "Back";// session->GetTrinityString(LANG_SLOT_NAME_BACK);
141+ case EQUIPMENT_SLOT_MAINHAND: return "Main hand";// session->GetTrinityString(LANG_SLOT_NAME_MAINHAND);
142+ case EQUIPMENT_SLOT_OFFHAND: return "Off hand";// session->GetTrinityString(LANG_SLOT_NAME_OFFHAND);
143+ case EQUIPMENT_SLOT_RANGED: return "Ranged";// session->GetTrinityString(LANG_SLOT_NAME_RANGED);
144+ case EQUIPMENT_SLOT_TABARD: return "Tabard";// session->GetTrinityString(LANG_SLOT_NAME_TABARD);
145+ default: return NULL;
146+ }
147+}
148+uint32 TransmogDisplayVendorMgr::GetSpecialPrice(ItemTemplate const* proto)
149+{
150+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetSpecialPrice");
151+
152+ uint32 cost = proto->SellPrice < 10000 ? 10000 : proto->SellPrice;
153+ return cost;
154+}
155+bool TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source)
156+{
157+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem");
158+
159+ if (!target || !source)
160+ return false;
161+
162+ if (source->ItemId == target->ItemId)
163+ return false;
164+
165+ if (source->DisplayInfoID == target->DisplayInfoID)
166+ return false;
167+
168+ if (source->Class != target->Class)
169+ return false;
170+
171+ if (source->InventoryType == INVTYPE_BAG ||
172+ source->InventoryType == INVTYPE_RELIC ||
173+ // source->InventoryType == INVTYPE_BODY ||
174+ source->InventoryType == INVTYPE_FINGER ||
175+ source->InventoryType == INVTYPE_TRINKET ||
176+ source->InventoryType == INVTYPE_AMMO ||
177+ source->InventoryType == INVTYPE_QUIVER)
178+ return false;
179+
180+ if (target->InventoryType == INVTYPE_BAG ||
181+ target->InventoryType == INVTYPE_RELIC ||
182+ // target->InventoryType == INVTYPE_BODY ||
183+ target->InventoryType == INVTYPE_FINGER ||
184+ target->InventoryType == INVTYPE_TRINKET ||
185+ target->InventoryType == INVTYPE_AMMO ||
186+ target->InventoryType == INVTYPE_QUIVER)
187+ return false;
188+
189+ if (!SuitableForTransmogrification(player, target) || !SuitableForTransmogrification(player, source)) // if (!transmogrified->CanTransmogrify() || !transmogrifier->CanBeTransmogrified())
190+ return false;
191+
192+ if (IsRangedWeapon(source->Class, source->SubClass) != IsRangedWeapon(target->Class, target->SubClass))
193+ return false;
194+
195+ if (source->SubClass != target->SubClass && !IsRangedWeapon(target->Class, target->SubClass))
196+ {
197+ if (source->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
198+ return false;
199+ if (source->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
200+ return false;
201+ }
202+
203+ if (source->InventoryType != target->InventoryType)
204+ {
205+ if (source->Class == ITEM_CLASS_WEAPON && !((IsRangedWeapon(target->Class, target->SubClass) ||
206+ ((target->InventoryType == INVTYPE_WEAPON || target->InventoryType == INVTYPE_2HWEAPON) &&
207+ (source->InventoryType == INVTYPE_WEAPON || source->InventoryType == INVTYPE_2HWEAPON)) ||
208+ ((target->InventoryType == INVTYPE_WEAPONMAINHAND || target->InventoryType == INVTYPE_WEAPONOFFHAND) &&
209+ (source->InventoryType == INVTYPE_WEAPON || source->InventoryType == INVTYPE_2HWEAPON)))))
210+ return false;
211+ if (source->Class == ITEM_CLASS_ARMOR &&
212+ !((source->InventoryType == INVTYPE_CHEST || source->InventoryType == INVTYPE_ROBE) &&
213+ (target->InventoryType == INVTYPE_CHEST || target->InventoryType == INVTYPE_ROBE)))
214+ return false;
215+ }
216+
217+ return true;
218+}
219+bool TransmogDisplayVendorMgr::SuitableForTransmogrification(Player* player, ItemTemplate const* proto)
220+{
221+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SuitableForTransmogrification");
222+
223+ // ItemTemplate const* proto = item->GetTemplate();
224+ if (!proto)
225+ return false;
226+
227+ if (proto->Class != ITEM_CLASS_ARMOR &&
228+ proto->Class != ITEM_CLASS_WEAPON)
229+ return false;
230+
231+ // Skip all checks for allowed items
232+ if (IsAllowed(proto->ItemId))
233+ return true;
234+
235+ if (IsNotAllowed(proto->ItemId))
236+ return false;
237+
238+ if (!AllowFishingPoles && proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
239+ return false;
240+
241+ if (!IsAllowedQuality(proto->Quality)) // (proto->Quality == ITEM_QUALITY_LEGENDARY)
242+ return false;
243+
244+ if (player)
245+ {
246+ if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && player->GetTeam() != HORDE)
247+ return false;
248+
249+ if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && player->GetTeam() != ALLIANCE)
250+ return false;
251+
252+ if (!IgnoreReqClass && (proto->AllowableClass & player->getClassMask()) == 0)
253+ return false;
254+
255+ if (!IgnoreReqRace && (proto->AllowableRace & player->getRaceMask()) == 0)
256+ return false;
257+
258+ if (!IgnoreReqSkill && proto->RequiredSkill != 0)
259+ {
260+ if (player->GetSkillValue(proto->RequiredSkill) == 0)
261+ return false;
262+ else if (player->GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
263+ return false;
264+ }
265+
266+ if (!IgnoreReqSpell && proto->RequiredSpell != 0 && !player->HasSpell(proto->RequiredSpell))
267+ return false;
268+
269+ if (!IgnoreReqLevel && player->getLevel() < proto->RequiredLevel)
270+ return false;
271+ }
272+
273+ // If World Event is not active, prevent using event dependant items
274+ if (!IgnoreReqEvent && proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
275+ return false;
276+
277+ if (!IgnoreReqStats)
278+ {
279+ if (!proto->RandomProperty && !proto->RandomSuffix)
280+ {
281+ bool found = false;
282+ for (uint8 i = 0; i < proto->StatsCount; ++i)
283+ {
284+ if (proto->ItemStat[i].ItemStatValue != 0)
285+ {
286+ found = true;
287+ break;
288+ }
289+ }
290+ if (!found)
291+ return false;
292+ }
293+ }
294+
295+ return true;
296+}
297+
298+bool TransmogDisplayVendorMgr::IsRangedWeapon(uint32 Class, uint32 SubClass)
299+{
300+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsRangedWeapon");
301+
302+ return Class == ITEM_CLASS_WEAPON && (
303+ SubClass == ITEM_SUBCLASS_WEAPON_BOW ||
304+ SubClass == ITEM_SUBCLASS_WEAPON_GUN ||
305+ SubClass == ITEM_SUBCLASS_WEAPON_CROSSBOW);
306+}
307+bool TransmogDisplayVendorMgr::IsAllowed(uint32 entry)
308+{
309+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowed");
310+
311+ return std::find(Allowed.begin(), Allowed.end(), entry) != Allowed.end();
312+}
313+bool TransmogDisplayVendorMgr::IsNotAllowed(uint32 entry)
314+{
315+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsNotAllowed");
316+
317+ return std::find(NotAllowed.begin(), NotAllowed.end(), entry) != NotAllowed.end();
318+}
319+bool TransmogDisplayVendorMgr::IsAllowedQuality(uint32 quality)
320+{
321+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowedQuality");
322+
323+ switch (quality)
324+ {
325+ case ITEM_QUALITY_POOR: return AllowPoor;
326+ case ITEM_QUALITY_NORMAL: return AllowCommon;
327+ case ITEM_QUALITY_UNCOMMON: return AllowUncommon;
328+ case ITEM_QUALITY_RARE: return AllowRare;
329+ case ITEM_QUALITY_EPIC: return AllowEpic;
330+ case ITEM_QUALITY_LEGENDARY: return AllowLegendary;
331+ case ITEM_QUALITY_ARTIFACT: return AllowArtifact;
332+ case ITEM_QUALITY_HEIRLOOM: return AllowHeirloom;
333+ default: return false;
334+ }
335+}
336+
337+void TransmogDisplayVendorMgr::HandleTransmogrify(Player* player, Creature* /*creature*/, uint32 vendorslot, uint32 itemEntry, bool no_cost)
338+{
339+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify");
340+
341+ SelectionStore::Selection selection;
342+ if (!selectionStore.GetSelection(player->GetGUID().GetCounter(), selection))
343+ return; // cheat, no slot selected
344+
345+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(selection.slot, player->GetSession());
346+ if (!slotname)
347+ return;
348+ uint8 slot = selection.slot;
349+
350+ // slot of the transmogrified item
351+ if (slot >= EQUIPMENT_SLOT_END)
352+ {
353+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify item %u with a wrong slot (%u) when transmogrifying items.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemEntry, slot);
354+ return; // LANG_ERR_TRANSMOG_INVALID_SLOT
355+ }
356+
357+ const ItemTemplate* itemTransmogrifier = NULL;
358+ // guid of the transmogrifier item, if it's not 0
359+ if (itemEntry)
360+ {
361+ itemTransmogrifier = sObjectMgr->GetItemTemplate(itemEntry);
362+ if (!itemTransmogrifier)
363+ {
364+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify with an invalid item entry %u.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemEntry);
365+ return; // LANG_ERR_TRANSMOG_MISSING_SRC_ITEM
366+ }
367+ }
368+
369+ // transmogrified item
370+ Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
371+ if (!itemTransmogrified)
372+ {
373+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), slot);
374+ player->GetSession()->SendNotification("No item in %s slot", slotname);
375+ return; // LANG_ERR_TRANSMOG_MISSING_DEST_ITEM
376+ }
377+
378+ if (!itemTransmogrifier) // reset look newEntry
379+ {
380+ DeleteFakeEntry(player, itemTransmogrified);
381+ }
382+ else
383+ {
384+ if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier))
385+ {
386+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemTransmogrified->GetEntry(), itemTransmogrifier->ItemId);
387+ player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
388+ return; // LANG_ERR_TRANSMOG_INVALID_ITEMS
389+ }
390+
391+ if (uint32 fakeEntry = GetFakeEntry(itemTransmogrified))
392+ {
393+ if (const ItemTemplate* fakeItemTemplate = sObjectMgr->GetItemTemplate(fakeEntry))
394+ {
395+ if (fakeItemTemplate->DisplayInfoID == itemTransmogrifier->DisplayInfoID)
396+ {
397+ player->GetSession()->SendNotification("%s already transmogrified with %s", slotname, getItemName(itemTransmogrifier, player->GetSession()).c_str());
398+ return;
399+ }
400+ }
401+ }
402+
403+ // {{entry}, {entry}, ...}
404+ std::list<uint32> L;
405+ uint32 counter = 0;
406+ bool over = false;
407+ if (itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
408+ {
409+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
410+ {
411+ const EntryVector* oM = optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
412+ if (!oM)
413+ continue;
414+ if (!over && counter + oM->size() < selection.offset)
415+ {
416+ counter += oM->size();
417+ }
418+ else
419+ {
420+ over = true;
421+ L.insert(L.end(), oM->begin(), oM->end());
422+ }
423+ }
424+ }
425+ else if (itemTransmogrified->GetTemplate()->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
426+ {
427+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
428+ {
429+ const EntryVector* oM = optionMap[i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
430+ if (!oM)
431+ continue;
432+ if (!over && counter + oM->size() < selection.offset)
433+ {
434+ counter += oM->size();
435+ }
436+ else
437+ {
438+ over = true;
439+ L.insert(L.end(), oM->begin(), oM->end());
440+ }
441+ }
442+ }
443+ else
444+ {
445+ const EntryVector* oM = optionMap[(itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTransmogrified->GetTemplate()->SubClass][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
446+ if (oM)
447+ {
448+ if (!over && counter + oM->size() < selection.offset)
449+ {
450+ counter += oM->size();
451+ }
452+ else
453+ {
454+ over = true;
455+ L.insert(L.end(), oM->begin(), oM->end());
456+ }
457+ }
458+ }
459+ std::list<uint32>::const_iterator it = L.begin();
460+ std::advance(it, (selection.offset - counter) + vendorslot);
461+ if (it == L.end() || (*it) != itemEntry)
462+ {
463+ player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
464+ return; // either cheat or changed items (not found in correct place in transmog vendor view)
465+ }
466+
467+ if (!no_cost)
468+ {
469+ if (RequireToken)
470+ {
471+ if (player->HasItemCount(TokenEntry, TokenAmount))
472+ {
473+ player->DestroyItemCount(TokenEntry, TokenAmount, true);
474+ }
475+ else
476+ {
477+ player->GetSession()->SendNotification("You do not have enough %ss", getItemName(sObjectMgr->GetItemTemplate(TransmogDisplayVendorMgr::TokenEntry), player->GetSession()).c_str());
478+ return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS
479+ }
480+ }
481+
482+ int32 cost = 0;
483+ cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
484+ cost *= ScaledCostModifier;
485+ cost += CopperCost;
486+
487+ if (cost) // 0 cost if reverting look
488+ {
489+ if (cost < 0)
490+ {
491+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) transmogrification invalid cost (non negative, amount %i). Transmogrified %u with %u", player->GetName().c_str(), player->GetGUID().ToString().c_str(), -cost, itemTransmogrified->GetEntry(), itemTransmogrifier->ItemId);
492+ }
493+ else
494+ {
495+ if (!player->HasEnoughMoney(cost))
496+ {
497+ player->GetSession()->SendNotification("You do not have enough money");
498+ return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY
499+ }
500+ player->ModifyMoney(-cost, false);
501+ }
502+ }
503+
504+ SetFakeEntry(player, itemTransmogrified, itemTransmogrifier->ItemId);
505+
506+ itemTransmogrified->UpdatePlayedTime(player);
507+
508+ itemTransmogrified->SetOwnerGUID(player->GetGUID());
509+ itemTransmogrified->SetNotRefundable(player);
510+ itemTransmogrified->ClearSoulboundTradeable(player);
511+
512+ //if (itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_USE)
513+ // itemTransmogrifier->SetBinding(true);
514+
515+ //itemTransmogrifier->SetOwnerGUID(player->GetGUID());
516+ //itemTransmogrifier->SetNotRefundable(player);
517+ //itemTransmogrifier->ClearSoulboundTradeable(player);
518+ }
519+
520+ player->PlayDirectSound(3337);
521+ player->GetSession()->SendAreaTriggerMessage("%s transmogrified", slotname);
522+ //return LANG_ERR_TRANSMOG_OK;
523+ }
524+}
525+
526+const char * TransmogDisplayVendorMgr::getQualityName(uint32 quality)
527+{
528+ switch (quality)
529+ {
530+ case ITEM_QUALITY_POOR: return "|CFF9d9d9d[Poor]";
531+ case ITEM_QUALITY_NORMAL: return "|CFFffffff[Common]";
532+ case ITEM_QUALITY_UNCOMMON: return "|CFF1eff00[Uncommon]";
533+ case ITEM_QUALITY_RARE: return "|CFF0070dd[Rare]";
534+ case ITEM_QUALITY_EPIC: return "|CFFa335ee[Epic]";
535+ case ITEM_QUALITY_LEGENDARY: return "|CFFff8000[Legendary]";
536+ case ITEM_QUALITY_ARTIFACT: return "|CFFe6cc80[Artifact]";
537+ case ITEM_QUALITY_HEIRLOOM: return "|CFFe5cc80[Heirloom]";
538+ default: return "[Unknown]";
539+ }
540+}
541+
542+std::string TransmogDisplayVendorMgr::getItemName(const ItemTemplate * itemTemplate, WorldSession * session)
543+{
544+ std::string name = itemTemplate->Name1;
545+ int loc_idx = session->GetSessionDbLocaleIndex();
546+ if (loc_idx >= 0)
547+ if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
548+ sObjectMgr->GetLocaleString(il->Name, loc_idx, name);
549+ return name;
550+}
551+
552+uint32 TransmogDisplayVendorMgr::getCorrectInvType(uint32 inventorytype)
553+{
554+ switch (inventorytype)
555+ {
556+ case INVTYPE_WEAPONMAINHAND:
557+ case INVTYPE_WEAPONOFFHAND:
558+ return INVTYPE_WEAPON;
559+ case INVTYPE_RANGEDRIGHT:
560+ return INVTYPE_RANGED;
561+ case INVTYPE_ROBE:
562+ return INVTYPE_CHEST;
563+ default:
564+ return inventorytype;
565+ }
566+}
567diff --git a/src/server/game/Entities/Item/TransmogDisplayVendorConf.h b/src/server/game/Entities/Item/TransmogDisplayVendorConf.h
568new file mode 100644
569index 0000000..496596d
570--- /dev/null
571+++ b/src/server/game/Entities/Item/TransmogDisplayVendorConf.h
572@@ -0,0 +1,160 @@
573+#ifndef DEF_TRANSMOGRIFICATION_DISPLAY_H
574+#define DEF_TRANSMOGRIFICATION_DISPLAY_H
575+
576+/*
577+Transmogrification display vendor
578+Code by Rochet2
579+Ideas LilleCarl
580+
581+ScriptName for NPC:
582+NPC_TransmogDisplayVendor
583+
584+Compatible with Transmogrification 6.1 by Rochet2
585+http://rochet2.github.io/Transmogrification
586+*/
587+
588+// use 0 or 1
589+#define TRANSMOGRIFICATION_ALREADY_INSTALLED 0
590+// Note! If you use both, set this to true (1) and in scriptloader make transmog load first
591+
592+#include "Define.h"
593+#include "ItemPrototype.h"
594+#include "SharedDefines.h"
595+#include <set>
596+#include <vector>
597+#include <unordered_map>
598+#include <boost/thread/locks.hpp>
599+#include <boost/thread/shared_mutex.hpp>
600+
601+class Creature;
602+class Item;
603+class Player;
604+class WorldSession;
605+struct ItemTemplate;
606+
607+enum TransmogDisplayVendorSenders
608+{
609+ SENDER_START = MAX_ITEM_QUALITY,
610+ SENDER_BACK,
611+ SENDER_SELECT_VENDOR,
612+ SENDER_REMOVE_ALL,
613+ SENDER_REMOVE_ONE,
614+ SENDER_REMOVE_MENU,
615+ SENDER_END,
616+};
617+
618+namespace
619+{
620+ class RWLockable
621+ {
622+ public:
623+ typedef boost::shared_mutex LockType;
624+ typedef boost::shared_lock<boost::shared_mutex> ReadGuard;
625+ typedef boost::unique_lock<boost::shared_mutex> WriteGuard;
626+ LockType& GetLock() { return _lock; }
627+ private:
628+ LockType _lock;
629+ };
630+};
631+
632+class TC_GAME_API SelectionStore : public RWLockable
633+{
634+public:
635+ struct Selection { uint32 item; uint8 slot; uint32 offset; uint32 quality; };
636+ typedef std::unordered_map<uint32, Selection> PlayerLowToSelection;
637+
638+ void SetSelection(uint32 playerLow, const Selection& selection)
639+ {
640+ WriteGuard guard(GetLock());
641+ hashmap[playerLow] = selection;
642+ }
643+
644+ bool GetSelection(uint32 playerLow, Selection& returnVal)
645+ {
646+ ReadGuard guard(GetLock());
647+
648+ PlayerLowToSelection::iterator it = hashmap.find(playerLow);
649+ if (it == hashmap.end())
650+ return false;
651+
652+ returnVal = it->second;
653+ return true;
654+ }
655+
656+ void RemoveSelection(uint32 playerLow)
657+ {
658+ WriteGuard guard(GetLock());
659+ hashmap.erase(playerLow);
660+ }
661+
662+private:
663+ PlayerLowToSelection hashmap;
664+};
665+
666+class TC_GAME_API TransmogDisplayVendorMgr
667+{
668+public:
669+ // Selection store
670+ static SelectionStore selectionStore; // selectionStore[lowGUID] = Selection
671+
672+ // Vendor data store
673+ // optionMap[Class? + SubClass][invtype][Quality] = EntryVector
674+ typedef std::vector<uint32> EntryVector;
675+ static EntryVector* optionMap[MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR][MAX_INVTYPE][MAX_ITEM_QUALITY];
676+
677+ static const std::set<uint32> AllowedItems;
678+ static const std::set<uint32> NotAllowedItems;
679+
680+ static const float ScaledCostModifier;
681+ static const int32 CopperCost;
682+
683+ static const bool RequireToken;
684+ static const uint32 TokenEntry;
685+ static const uint32 TokenAmount;
686+
687+ static const bool AllowPoor;
688+ static const bool AllowCommon;
689+ static const bool AllowUncommon;
690+ static const bool AllowRare;
691+ static const bool AllowEpic;
692+ static const bool AllowLegendary;
693+ static const bool AllowArtifact;
694+ static const bool AllowHeirloom;
695+
696+ static const bool AllowMixedArmorTypes;
697+ static const bool AllowMixedWeaponTypes;
698+ static const bool AllowFishingPoles;
699+
700+ static const bool IgnoreReqRace;
701+ static const bool IgnoreReqClass;
702+ static const bool IgnoreReqSkill;
703+ static const bool IgnoreReqSpell;
704+ static const bool IgnoreReqLevel;
705+ static const bool IgnoreReqEvent;
706+ static const bool IgnoreReqStats;
707+
708+ static std::vector<uint32> Allowed;
709+ static std::vector<uint32> NotAllowed;
710+
711+ static void HandleTransmogrify(Player* player, Creature* creature, uint32 vendorslot, uint32 itemEntry, bool no_cost = false);
712+
713+ static const char* getQualityName(uint32 quality);
714+ static std::string getItemName(const ItemTemplate* itemTemplate, WorldSession* session);
715+ static uint32 getCorrectInvType(uint32 inventorytype);
716+
717+ // From Transmogrification
718+ static uint32 GetFakeEntry(const Item* item);
719+ static void DeleteFakeEntry(Player* player, Item* item);
720+ static void SetFakeEntry(Player* player, Item* item, uint32 entry);
721+ static const char* getSlotName(uint8 slot, WorldSession* session);
722+ static void UpdateItem(Player* player, Item* item);
723+ static uint32 GetSpecialPrice(ItemTemplate const* proto);
724+ static bool CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source);
725+ static bool SuitableForTransmogrification(Player* player, ItemTemplate const* proto);
726+ static bool IsRangedWeapon(uint32 Class, uint32 SubClass);
727+ static bool IsAllowed(uint32 entry);
728+ static bool IsNotAllowed(uint32 entry);
729+ static bool IsAllowedQuality(uint32 quality);
730+};
731+
732+#endif
733diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
734index 50e27d9..3d5ecd9 100644
735--- a/src/server/game/Entities/Player/Player.cpp
736+++ b/src/server/game/Entities/Player/Player.cpp
737@@ -16,6 +16,7 @@
738 * with this program. If not, see <http://www.gnu.org/licenses/>.
739 */
740
741+#include "TransmogDisplayVendorConf.h"
742 #include "Player.h"
743 #include "AccountMgr.h"
744 #include "AchievementMgr.h"
745@@ -12069,7 +12070,10 @@ void Player::SetVisibleItemSlot(uint8 slot, Item* pItem)
746 {
747 if (pItem)
748 {
749- SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry());
750+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(pItem))
751+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), entry);
752+ else
753+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry());
754 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
755 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT));
756 }
757@@ -12197,6 +12201,7 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
758 {
759 if (Item* it = GetItemByPos(bag, slot))
760 {
761+ TransmogDisplayVendorMgr::DeleteFakeEntry(this, it);
762 ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
763 RemoveItem(bag, slot, update);
764 it->SetNotRefundable(this, false);
765@@ -21355,24 +21360,30 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
766 return false;
767 }
768
769- if (!(pProto->AllowableClass & getClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
770+ Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
771+ if (!creature)
772 {
773- SendBuyError(BUY_ERR_CANT_FIND_ITEM, nullptr, item, 0);
774+ TC_LOG_DEBUG("network", "Player::BuyItemFromVendorSlot: Vendor (%s) not found or player '%s' (%s) can't interact with him.",
775+ vendorguid.ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str());
776+ SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, nullptr, item, 0);
777 return false;
778 }
779
780- if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && GetTeam() == HORDE)))
781+ if (creature->GetScriptName() == "NPC_TransmogDisplayVendor")
782+ {
783+ TransmogDisplayVendorMgr::HandleTransmogrify(this, creature, vendorslot, item);
784 return false;
785+ }
786
787- Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
788- if (!creature)
789+ if (!(pProto->AllowableClass & getClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
790 {
791- TC_LOG_DEBUG("network", "Player::BuyItemFromVendorSlot: Vendor (%s) not found or player '%s' (%s) can't interact with him.",
792- vendorguid.ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str());
793- SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, nullptr, item, 0);
794+ SendBuyError(BUY_ERR_CANT_FIND_ITEM, nullptr, item, 0);
795 return false;
796 }
797
798+ if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && GetTeam() == HORDE)))
799+ return false;
800+
801 if (!sConditionMgr->IsObjectMeetingVendorItemConditions(creature->GetEntry(), item, this, creature))
802 {
803 TC_LOG_DEBUG("condition", "Player::BuyItemFromVendorSlot: Player '%s' (%s) doesn't meed conditions for creature (Entry: %u, Item: %u)",
804diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
805index 372a49b..a5d24af 100644
806--- a/src/server/game/Entities/Player/Player.h
807+++ b/src/server/game/Entities/Player/Player.h
808@@ -128,6 +128,18 @@ struct SpellModifier
809 Aura* const ownerAura;
810 };
811
812+typedef std::unordered_map<ObjectGuid, uint32> TransmogMapType;
813+
814+#ifdef PRESETS
815+typedef std::map<uint8, uint32> PresetslotMapType;
816+struct PresetData
817+{
818+ std::string name;
819+ PresetslotMapType slotMap; // slotMap[slotId] = entry
820+};
821+typedef std::map<uint8, PresetData> PresetMapType;
822+#endif
823+
824 typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
825 typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap;
826 typedef std::list<SpellModifier*> SpellModList;
827@@ -2271,6 +2283,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
828 std::string GetMapAreaAndZoneString() const;
829 std::string GetCoordsMapAreaAndZoneString() const;
830
831+ TransmogMapType transmogMap; // transmogMap[iGUID] = entry
832+#ifdef PRESETS
833+ PresetMapType presetMap; // presetMap[presetId] = presetData
834+#endif
835+
836 protected:
837 // Gamemaster whisper whitelist
838 GuidList WhisperList;
839diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
840index e07e10a..3d8d654 100644
841--- a/src/server/game/Handlers/SpellHandler.cpp
842+++ b/src/server/game/Handlers/SpellHandler.cpp
843@@ -16,6 +16,7 @@
844 * with this program. If not, see <http://www.gnu.org/licenses/>.
845 */
846
847+#include "TransmogDisplayVendorConf.h"
848 #include "Common.h"
849 #include "DBCStores.h"
850 #include "WorldPacket.h"
851@@ -625,7 +626,12 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
852 else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
853 data << uint32(0);
854 else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
855- data << uint32(item->GetTemplate()->DisplayInfoID);
856+ {
857+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(item))
858+ data << uint32(sObjectMgr->GetItemTemplate(entry)->DisplayInfoID);
859+ else
860+ data << uint32(item->GetTemplate()->DisplayInfoID);
861+ }
862 else
863 data << uint32(0);
864 }
865diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/README.md b/src/server/scripts/Custom/TransmogDisplayVendor/README.md
866new file mode 100644
867index 0000000..de921d6
868--- /dev/null
869+++ b/src/server/scripts/Custom/TransmogDisplayVendor/README.md
870@@ -0,0 +1,49 @@
871+#Transmogrification Display Vendor [](https://travis-ci.org/Rochet2/TrinityCore)
872+
873+####About
874+Original idea by LilleCarl.
875+Coding work and execution by Rochet2.
876+Transmogrification Display Vendor allows you to change the display of an item to something else.
877+You can use any item display in the game, as long as it fits the requirements.
878+Requirements can be tweaked in the `TransmogDisplayVendor.cpp` file.
879+Basically any item should work with transmogrification. Custom items as well. No item is hardcoded to the system.
880+Has a feature to work with the regular [Transmogrification](http://rochet2.github.io/Transmogrification.html). This can be enabled before compiling in `TransmogDisplayVendor.h`.
881+Made for 3.3.5a.<br />
882+Source: http://www.trinitycore.org/f/topic/7993-transmogrification-display-vendor/
883+
884+Video: https://youtu.be/PIheEziN_dY
885+
886+####Installation
887+
888+Available as:
889+- Direct merge: https://github.com/Rochet2/TrinityCore/tree/transmogvendor
890+- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...transmogvendor.diff
891+- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...transmogvendor
892+
893+Using direct merge:
894+- open git bash to source location
895+- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git`
896+- do `git pull rochet2 transmogvendor`
897+- use cmake and compile
898+
899+Using diff *(recommended)*:
900+- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail.
901+- download the diff by __right clicking__ the link and select __Save link as__
902+- place the downloaded `transmogvendor.diff` to the source root folder
903+- open git bash to source location
904+- do `git apply transmogvendor.diff`
905+ - if using the regular transmogrification, simply use --reject with either and overwrite the parts of the other. Order doesnt matter, as long as duplicate code doesnt exist.
906+- use cmake and compile
907+
908+After compiling:
909+- Navigate to `\src\server\scripts\Custom\TransmogDisplayVendor\sql\`
910+- Run `characters.sql` to your characters database
911+ - This is same file as with the regular transmog
912+- Optionally you can also insert a transmogrifier NPC to your database by running `world_NPC.sql` to your world database.
913+
914+####Usage
915+Equip an item that is suitable for transmogrification.
916+Talk to Transmogrifier and select the item slot. Then select the quality and then the item you want to transmogrify to.
917+
918+####Bugs and Contact
919+Report issues and similar to http://rochet2.github.io/
920diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp b/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp
921new file mode 100644
922index 0000000..cbc604c
923--- /dev/null
924+++ b/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp
925@@ -0,0 +1,553 @@
926+/*
927+Transmog display vendor
928+Code by Rochet2
929+Ideas LilleCarl
930+
931+ScriptName for NPC:
932+NPC_TransmogDisplayVendor
933+
934+Compatible with Transmogrification 6.1 by Rochet2
935+http://rochet2.github.io/Transmogrification
936+*/
937+
938+#include "TransmogDisplayVendorConf.h"
939+#include "Bag.h"
940+#include "Common.h"
941+#include "Config.h"
942+#include "Creature.h"
943+#include "DatabaseEnv.h"
944+#include "DBCStructure.h"
945+#include "Define.h"
946+#include "Field.h"
947+#include "GameEventMgr.h"
948+#include "GossipDef.h"
949+#include "Item.h"
950+#include "ItemPrototype.h"
951+#include "Language.h"
952+#include "Log.h"
953+#include "Player.h"
954+#include "ObjectGuid.h"
955+#include "ObjectMgr.h"
956+#include "QueryResult.h"
957+#include "ScriptedGossip.h"
958+#include "ScriptMgr.h"
959+#include "SharedDefines.h"
960+#include "Transaction.h"
961+#include "WorldSession.h"
962+#include <sstream>
963+#include <string>
964+
965+class NPC_TransmogDisplayVendor : public CreatureScript
966+{
967+public:
968+ NPC_TransmogDisplayVendor() : CreatureScript("NPC_TransmogDisplayVendor") { } // If you change this, also change in Player.cpp: if (creature->GetScriptName() == "NPC_TransmogDisplayVendor")
969+
970+ bool OnGossipHello(Player* player, Creature* creature) override
971+ {
972+ player->PlayerTalkClass->ClearMenus();
973+ TransmogDisplayVendorMgr::selectionStore.RemoveSelection(player->GetGUID().GetCounter());
974+ WorldSession* session = player->GetSession();
975+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
976+ {
977+ // if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
978+ if (const char* slotName = TransmogDisplayVendorMgr::getSlotName(slot, session))
979+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, slotName, SENDER_SELECT_VENDOR, slot);
980+ }
981+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, "Remove transmogrifications", SENDER_REMOVE_MENU, 0);
982+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
983+ return true;
984+ }
985+
986+ bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
987+ {
988+ WorldSession* session = player->GetSession();
989+ player->PlayerTalkClass->ClearMenus();
990+ switch (sender)
991+ {
992+ case SENDER_SELECT_VENDOR: // action = slot
993+ {
994+ Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action);
995+ if (!item)
996+ {
997+ if (const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession()))
998+ session->SendNotification("No item equipped in %s slot", slotname);
999+ OnGossipHello(player, creature);
1000+ return true;
1001+ }
1002+ const ItemTemplate * itemTemplate = item->GetTemplate();
1003+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_INTERACT_1, (std::string)"Update selected; " + TransmogDisplayVendorMgr::getItemName(itemTemplate, session), sender, action);
1004+
1005+ // [quality] = {size}
1006+ std::map<uint32, uint32> L;
1007+ if (itemTemplate->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
1008+ {
1009+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
1010+ {
1011+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
1012+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
1013+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
1014+ if (*oM)
1015+ L[i] += (*oM)->size();
1016+ }
1017+ }
1018+ else if (itemTemplate->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
1019+ {
1020+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
1021+ {
1022+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
1023+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
1024+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
1025+ if (*oM)
1026+ L[i] += (*oM)->size();
1027+ }
1028+ }
1029+ else
1030+ {
1031+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
1032+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
1033+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
1034+ if (*oM)
1035+ L[i] += (*oM)->size();
1036+ }
1037+
1038+ for (std::map<uint32, uint32>::const_iterator it = L.begin(); it != L.end(); ++it)
1039+ {
1040+ for (uint32 count = 0; count*MAX_VENDOR_ITEMS < it->second; ++count)
1041+ {
1042+ std::ostringstream ss;
1043+ ss << TransmogDisplayVendorMgr::getQualityName(it->first);
1044+ if (count)
1045+ ss << " [" << count << "]";
1046+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, ss.str().c_str(), it->first, count*MAX_VENDOR_ITEMS);
1047+ }
1048+ }
1049+
1050+ if (player->PlayerTalkClass->GetGossipMenu().GetMenuItemCount() <= 1)
1051+ {
1052+ if (const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession()))
1053+ session->SendNotification("No transmogrifications available for %s", slotname);
1054+ OnGossipHello(player, creature);
1055+ return true;
1056+ }
1057+
1058+ SelectionStore::Selection temp = { item->GetEntry(), static_cast<uint8>(action), 0, 0 }; // entry, slot, offset, quality
1059+ TransmogDisplayVendorMgr::selectionStore.SetSelection(player->GetGUID().GetCounter(), temp);
1060+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TALK, "Back..", SENDER_BACK, 0);
1061+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
1062+ } break;
1063+ case SENDER_BACK: // Back
1064+ {
1065+ OnGossipHello(player, creature);
1066+ } break;
1067+ case SENDER_REMOVE_ALL: // Remove TransmogDisplayVendorMgrs
1068+ {
1069+ bool removed = false;
1070+ for (uint8 Slot = EQUIPMENT_SLOT_START; Slot < EQUIPMENT_SLOT_END; Slot++)
1071+ {
1072+ if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, Slot))
1073+ {
1074+ if (!TransmogDisplayVendorMgr::GetFakeEntry(newItem))
1075+ continue;
1076+ TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
1077+ removed = true;
1078+ }
1079+ }
1080+ if (removed)
1081+ {
1082+ session->SendAreaTriggerMessage("Transmogrifications removed from equipped items");
1083+ player->PlayDirectSound(3337);
1084+ }
1085+ else
1086+ {
1087+ session->SendNotification("You have no transmogrified items equipped");
1088+ }
1089+ OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
1090+ } break;
1091+ case SENDER_REMOVE_ONE: // Remove TransmogDisplayVendorMgr from single item
1092+ {
1093+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession());
1094+ if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
1095+ {
1096+ if (TransmogDisplayVendorMgr::GetFakeEntry(newItem))
1097+ {
1098+ TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
1099+ if (slotname)
1100+ session->SendAreaTriggerMessage("%s transmogrification removed", slotname);
1101+ player->PlayDirectSound(3337);
1102+ }
1103+ else if (slotname)
1104+ {
1105+ session->SendNotification("No transmogrification on %s slot", slotname);
1106+ }
1107+ }
1108+ else if (slotname)
1109+ {
1110+ session->SendNotification("No item equipped in %s slot", slotname);
1111+ }
1112+ OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
1113+ } break;
1114+ case SENDER_REMOVE_MENU:
1115+ {
1116+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
1117+ {
1118+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(slot, player->GetSession());
1119+ if (!slotname)
1120+ continue;
1121+ std::ostringstream ss;
1122+ ss << "Remove transmogrification from " << slotname << "?";
1123+ player->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_INTERACT_1, (std::string)"Remove from " + slotname, SENDER_REMOVE_ONE, slot, ss.str().c_str(), 0, false);
1124+ }
1125+ player->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_INTERACT_1, "Remove all transmogrifications", SENDER_REMOVE_ALL, 0, "Are you sure you want to remove all transmogrifications?", 0, false);
1126+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TALK, "Back..", SENDER_BACK, 0);
1127+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
1128+ } break;
1129+ default: // Show items you can use
1130+ {
1131+ if (sender >= MAX_ITEM_QUALITY) // sender = quality, action = iterator
1132+ return false; // cheat
1133+
1134+ SelectionStore::Selection selection;
1135+ if (!TransmogDisplayVendorMgr::selectionStore.GetSelection(player->GetGUID().GetCounter(), selection))
1136+ return false; // cheat
1137+ if (selection.offset != 0 || selection.quality != 0)
1138+ return false; // cheat (something is off)
1139+
1140+ selection.offset = action;
1141+ selection.quality = sender;
1142+ uint32 slot = selection.slot; // slot
1143+ TransmogDisplayVendorMgr::selectionStore.SetSelection(player->GetGUID().GetCounter(), selection);
1144+
1145+ if (const ItemTemplate* itemTemplate = sObjectMgr->GetItemTemplate(selection.item))
1146+ {
1147+ if (!TransmogDisplayVendorMgr::SuitableForTransmogrification(player, itemTemplate))
1148+ {
1149+ player->GetSession()->SendNotification("Equipped item is not suitable for transmogrification");
1150+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
1151+ return true;
1152+ }
1153+
1154+ // {{entry}, {entry}, ...}
1155+ std::list<uint32> L;
1156+ uint32 counter = 0;
1157+ bool over = false;
1158+ if (itemTemplate->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
1159+ {
1160+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
1161+ {
1162+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
1163+ if (!oM)
1164+ continue;
1165+ if (!over && counter + oM->size() < selection.offset)
1166+ {
1167+ counter += oM->size();
1168+ }
1169+ else
1170+ {
1171+ over = true;
1172+ L.insert(L.end(), oM->begin(), oM->end());
1173+ }
1174+ }
1175+ }
1176+ else if (itemTemplate->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
1177+ {
1178+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
1179+ {
1180+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
1181+ if (!oM)
1182+ continue;
1183+ if (!over && counter + oM->size() < selection.offset)
1184+ counter += oM->size();
1185+ else
1186+ {
1187+ over = true;
1188+ L.insert(L.end(), oM->begin(), oM->end());
1189+ }
1190+ }
1191+ }
1192+ else
1193+ {
1194+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
1195+ if (oM)
1196+ {
1197+ if (!over && counter + oM->size() < selection.offset)
1198+ {
1199+ counter += oM->size();
1200+ }
1201+ else
1202+ {
1203+ over = true;
1204+ L.insert(L.end(), oM->begin(), oM->end());
1205+ }
1206+ }
1207+ }
1208+
1209+ // TransmogDisplayVendorMgr::EntryVector oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
1210+ uint32 itemCount = L.size() - (selection.offset - counter);
1211+ if (itemCount > MAX_VENDOR_ITEMS)
1212+ itemCount = MAX_VENDOR_ITEMS;
1213+
1214+ if (!itemCount)
1215+ {
1216+ session->SendAreaTriggerMessage("No items found");
1217+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
1218+ return true;
1219+ }
1220+ player->CLOSE_GOSSIP_MENU();
1221+
1222+ TC_LOG_DEBUG("network", "WORLD: Sent SMSG_LIST_INVENTORY");
1223+
1224+ Creature* vendor = player->GetNPCIfCanInteractWith(creature->GetGUID(), UNIT_NPC_FLAG_VENDOR);
1225+ if (!vendor)
1226+ {
1227+ TC_LOG_DEBUG("network", "WORLD: SendListInventory - Unit (GUID: %u) not found or you can not interact with him.", creature->GetGUID().GetCounter());
1228+ player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, ObjectGuid::Empty, 0);
1229+ return true;
1230+ }
1231+
1232+ if (player->HasUnitState(UNIT_STATE_DIED))
1233+ player->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
1234+
1235+ if (vendor->HasUnitState(UNIT_STATE_MOVING))
1236+ vendor->StopMoving();
1237+
1238+ uint8 count = 0;
1239+
1240+ WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4);
1241+ data << uint64(creature->GetGUID());
1242+
1243+ size_t countPos = data.wpos();
1244+ data << uint8(count);
1245+
1246+ uint32 item_amount = 0;
1247+ std::list<uint32>::const_iterator it = L.begin();
1248+ std::advance(it, (selection.offset - counter));
1249+ for (; it != L.end() && count < itemCount; ++it, ++count)
1250+ {
1251+ if (ItemTemplate const* curtemp = sObjectMgr->GetItemTemplate(*it))
1252+ {
1253+ if (!TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(player, itemTemplate, curtemp))
1254+ continue;
1255+
1256+ data << uint32(count + 1);
1257+ data << uint32(curtemp->ItemId);
1258+ data << uint32(curtemp->DisplayInfoID);
1259+ data << int32(0xFFFFFFFF);
1260+ data << uint32(0);
1261+ data << uint32(curtemp->MaxDurability);
1262+ data << uint32(curtemp->BuyCount);
1263+ data << uint32(0);
1264+ ++item_amount;
1265+ }
1266+ }
1267+
1268+ if (!item_amount)
1269+ {
1270+ session->SendAreaTriggerMessage("No transmogrifications found for equipped item");
1271+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
1272+ return true;
1273+ }
1274+ else
1275+ {
1276+ data.put<uint8>(countPos, item_amount);
1277+ session->SendPacket(&data);
1278+ }
1279+ }
1280+ else
1281+ {
1282+ session->SendNotification("Invalid item equipped");
1283+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
1284+ return true;
1285+ }
1286+ } break;
1287+ }
1288+ return true;
1289+ }
1290+};
1291+
1292+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
1293+class Player_Transmogrify : public PlayerScript
1294+{
1295+public:
1296+ Player_Transmogrify() : PlayerScript("Player_Transmogrify") { }
1297+
1298+ std::vector<ObjectGuid> GetItemList(const Player* player) const
1299+ {
1300+ std::vector<ObjectGuid> itemlist;
1301+
1302+ // Copy paste from Player::GetItemByGuid(guid)
1303+
1304+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
1305+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
1306+ itemlist.push_back(pItem->GetGUID());
1307+
1308+ for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
1309+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
1310+ itemlist.push_back(pItem->GetGUID());
1311+
1312+ for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
1313+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
1314+ itemlist.push_back(pItem->GetGUID());
1315+
1316+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
1317+ if (Bag* pBag = player->GetBagByPos(i))
1318+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
1319+ if (Item* pItem = pBag->GetItemByPos(j))
1320+ itemlist.push_back(pItem->GetGUID());
1321+
1322+ for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
1323+ if (Bag* pBag = player->GetBagByPos(i))
1324+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
1325+ if (Item* pItem = pBag->GetItemByPos(j))
1326+ itemlist.push_back(pItem->GetGUID());
1327+
1328+ return itemlist;
1329+ }
1330+
1331+ void OnSave(Player* player) override
1332+ {
1333+ uint32 lowguid = player->GetGUID().GetCounter();
1334+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
1335+ trans->PAppend("DELETE FROM `custom_transmogrification` WHERE `Owner` = %u", lowguid);
1336+
1337+ if (!player->transmogMap.empty())
1338+ {
1339+ // Only save items that are in inventory / bank / etc
1340+ std::vector<ObjectGuid> items = GetItemList(player);
1341+ for (std::vector<ObjectGuid>::const_iterator it = items.begin(); it != items.end(); ++it)
1342+ {
1343+ TransmogMapType::const_iterator it2 = player->transmogMap.find(*it);
1344+ if (it2 == player->transmogMap.end())
1345+ continue;
1346+
1347+ trans->PAppend("REPLACE INTO custom_transmogrification (GUID, FakeEntry, Owner) VALUES (%u, %u, %u)", it2->first.GetCounter(), it2->second, lowguid);
1348+ }
1349+ }
1350+
1351+ if (trans->GetSize()) // basically never false
1352+ CharacterDatabase.CommitTransaction(trans);
1353+ }
1354+
1355+ void OnLogin(Player* player, bool /*firstLogin*/) override
1356+ {
1357+ QueryResult result = CharacterDatabase.PQuery("SELECT GUID, FakeEntry FROM custom_transmogrification WHERE Owner = %u", player->GetGUID().GetCounter());
1358+
1359+ if (result)
1360+ {
1361+ do
1362+ {
1363+ Field* field = result->Fetch();
1364+ ObjectGuid itemGUID(HighGuid::Item, 0, field[0].GetUInt32());
1365+ uint32 fakeEntry = field[1].GetUInt32();
1366+ // Only load items that are in inventory / bank / etc
1367+ if (sObjectMgr->GetItemTemplate(fakeEntry) && player->GetItemByGuid(itemGUID))
1368+ {
1369+ player->transmogMap[itemGUID] = fakeEntry;
1370+ }
1371+ else
1372+ {
1373+ // Ignore, will be erased on next save.
1374+ // Additionally this can happen if an item was deleted from DB but still exists for the player
1375+ // TC_LOG_ERROR("custom.transmog", "Item entry (Entry: %u, itemGUID: %u, playerGUID: %u) does not exist, ignoring.", fakeEntry, GUID_LOPART(itemGUID), player->GetGUID().GetCounter());
1376+ // CharacterDatabase.PExecute("DELETE FROM custom_transmogrification WHERE FakeEntry = %u", fakeEntry);
1377+ }
1378+ } while (result->NextRow());
1379+
1380+ if (!player->transmogMap.empty())
1381+ {
1382+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
1383+ {
1384+ if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
1385+ {
1386+ player->SetVisibleItemSlot(slot, item);
1387+ if (player->IsInWorld())
1388+ item->SendUpdateToPlayer(player);
1389+ }
1390+ }
1391+ }
1392+ }
1393+ }
1394+
1395+ void OnLogout(Player* player) override
1396+ {
1397+ TransmogDisplayVendorMgr::selectionStore.RemoveSelection(player->GetGUID().GetCounter());
1398+ }
1399+};
1400+#endif
1401+
1402+class PREP_TransmogDisplayVendor : public WorldScript
1403+{
1404+public:
1405+ PREP_TransmogDisplayVendor() : WorldScript("PREP_TransmogDisplayVendor") { }
1406+
1407+ void OnStartup() override
1408+ {
1409+ for (uint32 v : TransmogDisplayVendorMgr::AllowedItems)
1410+ TransmogDisplayVendorMgr::Allowed.push_back(v);
1411+ for (uint32 v : TransmogDisplayVendorMgr::NotAllowedItems)
1412+ TransmogDisplayVendorMgr::NotAllowed.push_back(v);
1413+
1414+ TC_LOG_INFO("server.loading", "Creating a list of usable transmogrification entries...");
1415+ // initialize .. for reload in future?
1416+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
1417+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
1418+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
1419+ delete TransmogDisplayVendorMgr::optionMap[i][j][k], TransmogDisplayVendorMgr::optionMap[i][j][k] = NULL;
1420+
1421+ std::unordered_set<uint32> displays;
1422+ ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore();
1423+ for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr)
1424+ {
1425+ if (itr->second.Class != ITEM_CLASS_WEAPON && itr->second.Class != ITEM_CLASS_ARMOR)
1426+ continue;
1427+ if (!TransmogDisplayVendorMgr::SuitableForTransmogrification(NULL, &itr->second))
1428+ continue;
1429+ if (displays.find(itr->second.DisplayInfoID) != displays.end()) // skip duplicate item displays
1430+ continue;
1431+ TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[(itr->second.Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itr->second.SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itr->second.InventoryType)][itr->second.Quality];
1432+ if (!oM)
1433+ {
1434+ oM = new TransmogDisplayVendorMgr::EntryVector();
1435+ TransmogDisplayVendorMgr::optionMap[(itr->second.Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itr->second.SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itr->second.InventoryType)][itr->second.Quality] = oM;
1436+ }
1437+ if (oM->size() < MAX_VENDOR_ITEMS * 3)
1438+ {
1439+ oM->push_back(itr->second.ItemId);
1440+ displays.insert(itr->second.DisplayInfoID);
1441+ }
1442+ else
1443+ {
1444+ TC_LOG_INFO("server.loading", "Too many items for transmogrification: Class: %u SubClass: %u InventoryType: %u Quality: %u", itr->second.Class, itr->second.SubClass, TransmogDisplayVendorMgr::getCorrectInvType(itr->second.InventoryType), itr->second.Quality);
1445+ }
1446+ }
1447+
1448+ // resize entry lists
1449+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
1450+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
1451+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
1452+ if (TransmogDisplayVendorMgr::optionMap[i][j][k])
1453+ TransmogDisplayVendorMgr::optionMap[i][j][k]->resize(TransmogDisplayVendorMgr::optionMap[i][j][k]->size());
1454+
1455+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
1456+ TC_LOG_INFO("custom.transmog", "Deleting non-existing transmogrification entries...");
1457+ CharacterDatabase.DirectExecute("DELETE FROM custom_transmogrification WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_transmogrification.GUID)");
1458+#endif
1459+ }
1460+
1461+ void OnShutdown() override
1462+ {
1463+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
1464+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
1465+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
1466+ delete TransmogDisplayVendorMgr::optionMap[i][j][k], TransmogDisplayVendorMgr::optionMap[i][j][k] = NULL;
1467+ }
1468+};
1469+
1470+void AddSC_NPC_TransmogDisplayVendor()
1471+{
1472+ new NPC_TransmogDisplayVendor();
1473+ new PREP_TransmogDisplayVendor();
1474+
1475+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
1476+ new Player_Transmogrify();
1477+#endif
1478+}
1479diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/sql/characters.sql b/src/server/scripts/Custom/TransmogDisplayVendor/sql/characters.sql
1480new file mode 100644
1481index 0000000..4529815
1482--- /dev/null
1483+++ b/src/server/scripts/Custom/TransmogDisplayVendor/sql/characters.sql
1484@@ -0,0 +1,37 @@
1485+-- --------------------------------------------------------
1486+-- Host: localhost
1487+-- Server version: 5.5.39 - MySQL Community Server (GPL)
1488+-- Server OS: Win32
1489+-- HeidiSQL Version: 9.1.0.4894
1490+-- --------------------------------------------------------
1491+
1492+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
1493+/*!40101 SET NAMES utf8mb4 */;
1494+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
1495+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
1496+
1497+-- Dumping structure for table tc_c.custom_transmogrification
1498+CREATE TABLE IF NOT EXISTS `custom_transmogrification` (
1499+ `GUID` int(10) unsigned NOT NULL COMMENT 'Item guidLow',
1500+ `FakeEntry` int(10) unsigned NOT NULL COMMENT 'Item entry',
1501+ `Owner` int(10) unsigned NOT NULL COMMENT 'Player guidLow',
1502+ PRIMARY KEY (`GUID`),
1503+ KEY `Owner` (`Owner`)
1504+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='6_2';
1505+
1506+-- Data exporting was unselected.
1507+
1508+
1509+-- Dumping structure for table tc_c.custom_transmogrification_sets
1510+CREATE TABLE IF NOT EXISTS `custom_transmogrification_sets` (
1511+ `Owner` int(10) unsigned NOT NULL COMMENT 'Player guidlow',
1512+ `PresetID` tinyint(3) unsigned NOT NULL COMMENT 'Preset identifier',
1513+ `SetName` text COMMENT 'SetName',
1514+ `SetData` text COMMENT 'Slot1 Entry1 Slot2 Entry2',
1515+ PRIMARY KEY (`Owner`,`PresetID`)
1516+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='6_1';
1517+
1518+-- Data exporting was unselected.
1519+/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
1520+/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
1521+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
1522diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/sql/updates/characters_update_6_1_to_6_2.sql b/src/server/scripts/Custom/TransmogDisplayVendor/sql/updates/characters_update_6_1_to_6_2.sql
1523new file mode 100644
1524index 0000000..0e675b5
1525--- /dev/null
1526+++ b/src/server/scripts/Custom/TransmogDisplayVendor/sql/updates/characters_update_6_1_to_6_2.sql
1527@@ -0,0 +1,3 @@
1528+ALTER TABLE `custom_transmogrification`
1529+ COMMENT='6_2',
1530+ COLLATE='utf8_general_ci';
1531diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql b/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql
1532new file mode 100644
1533index 0000000..353e593
1534--- /dev/null
1535+++ b/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql
1536@@ -0,0 +1,6 @@
1537+SET
1538+@Entry = 190011,
1539+@Name = "Narpweaver";
1540+
1541+INSERT INTO `creature_template` (`entry`, `modelid1`, `modelid2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `AIName`, `MovementType`, `InhabitType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES
1542+(@Entry, 19646, 0, @Name, "Transmogrifier", NULL, 0, 80, 80, 2, 35, 129, 1, 0, 0, 2000, 0, 1, 0, 7, 138936390, 0, 0, 0, '', 0, 3, 1, 0, 0, 1, 0, 0, 'NPC_TransmogDisplayVendor');
1543diff --git a/src/server/scripts/Custom/custom_script_loader.cpp b/src/server/scripts/Custom/custom_script_loader.cpp
1544index dd4b5e9..0ea2524 100644
1545--- a/src/server/scripts/Custom/custom_script_loader.cpp
1546+++ b/src/server/scripts/Custom/custom_script_loader.cpp
1547@@ -16,10 +16,12 @@
1548 */
1549
1550 // This is where scripts' loading functions should be declared:
1551+void AddSC_NPC_TransmogDisplayVendor();
1552
1553
1554 // The name of this function should match:
1555 // void Add${NameOfDirectory}Scripts()
1556 void AddCustomScripts()
1557 {
1558+ AddSC_NPC_TransmogDisplayVendor();
1559 }
1560diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
1561index 8b8c530..fdd939d 100644
1562--- a/src/server/scripts/Spells/spell_generic.cpp
1563+++ b/src/server/scripts/Spells/spell_generic.cpp
1564@@ -22,6 +22,7 @@
1565 * Scriptnames of files in this file should be prefixed with "spell_gen_"
1566 */
1567
1568+#include "TransmogDisplayVendorConf.h"
1569 #include "ScriptMgr.h"
1570 #include "Battleground.h"
1571 #include "Cell.h"
1572@@ -861,7 +862,12 @@ class spell_gen_clone_weapon_aura : public SpellScriptLoader
1573 if (Player* player = caster->ToPlayer())
1574 {
1575 if (Item* mainItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
1576- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry());
1577+ {
1578+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(mainItem))
1579+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, entry);
1580+ else
1581+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry());
1582+ }
1583 }
1584 else
1585 target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID));
1586@@ -875,7 +881,12 @@ class spell_gen_clone_weapon_aura : public SpellScriptLoader
1587 if (Player* player = caster->ToPlayer())
1588 {
1589 if (Item* offItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
1590- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry());
1591+ {
1592+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(offItem))
1593+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, entry);
1594+ else
1595+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry());
1596+ }
1597 }
1598 else
1599 target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1));
1600@@ -888,7 +899,12 @@ class spell_gen_clone_weapon_aura : public SpellScriptLoader
1601 if (Player* player = caster->ToPlayer())
1602 {
1603 if (Item* rangedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
1604- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry());
1605+ {
1606+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(rangedItem))
1607+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, entry);
1608+ else
1609+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry());
1610+ }
1611 }
1612 else
1613 target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2));