eluna-scripts-ornfelt/lua/WowEmulationScriptPack/patch/transmogvendor_3.3.5.diff
2024-09-23 07:59:23 +02:00

1630 lines
74 KiB
Diff

diff --git a/sql/custom/characters/2017_05_03_00_characters_transmogvendor.sql b/sql/custom/characters/2017_05_03_00_characters_transmogvendor.sql
new file mode 100644
index 000000000000..452981534079
--- /dev/null
+++ b/sql/custom/characters/2017_05_03_00_characters_transmogvendor.sql
@@ -0,0 +1,37 @@
+-- --------------------------------------------------------
+-- Host: localhost
+-- Server version: 5.5.39 - MySQL Community Server (GPL)
+-- Server OS: Win32
+-- HeidiSQL Version: 9.1.0.4894
+-- --------------------------------------------------------
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET NAMES utf8mb4 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+
+-- Dumping structure for table tc_c.custom_transmogrification
+CREATE TABLE IF NOT EXISTS `custom_transmogrification` (
+ `GUID` int(10) unsigned NOT NULL COMMENT 'Item guidLow',
+ `FakeEntry` int(10) unsigned NOT NULL COMMENT 'Item entry',
+ `Owner` int(10) unsigned NOT NULL COMMENT 'Player guidLow',
+ PRIMARY KEY (`GUID`),
+ KEY `Owner` (`Owner`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='6_2';
+
+-- Data exporting was unselected.
+
+
+-- Dumping structure for table tc_c.custom_transmogrification_sets
+CREATE TABLE IF NOT EXISTS `custom_transmogrification_sets` (
+ `Owner` int(10) unsigned NOT NULL COMMENT 'Player guidlow',
+ `PresetID` tinyint(3) unsigned NOT NULL COMMENT 'Preset identifier',
+ `SetName` text COMMENT 'SetName',
+ `SetData` text COMMENT 'Slot1 Entry1 Slot2 Entry2',
+ PRIMARY KEY (`Owner`,`PresetID`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='6_1';
+
+-- Data exporting was unselected.
+/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
+/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
diff --git a/src/server/game/Entities/Item/TransmogDisplayVendor.cpp b/src/server/game/Entities/Item/TransmogDisplayVendor.cpp
new file mode 100644
index 000000000000..567e412c33ae
--- /dev/null
+++ b/src/server/game/Entities/Item/TransmogDisplayVendor.cpp
@@ -0,0 +1,565 @@
+/*
+Transmog display vendor
+Code by Rochet2
+Ideas LilleCarl
+
+ScriptName for NPC:
+NPC_TransmogDisplayVendor
+
+Compatible with Transmogrification 6.1 by Rochet2
+http://rochet2.github.io/Transmogrification
+*/
+
+#include "TransmogDisplayVendorConf.h"
+#include "Common.h"
+#include "QueryResult.h"
+#include "DatabaseEnv.h"
+#include "Transaction.h"
+#include "Field.h"
+#include "DBCStructure.h"
+#include "Item.h"
+#include "ItemTemplate.h"
+#include "Language.h"
+#include "Log.h"
+#include "Player.h"
+#include "ObjectGuid.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "GameEventMgr.h"
+#include <sstream>
+#include <string>
+
+// Config start
+
+// Edit Transmogrification compatibility in TransmogDisplayVendorConf.h
+
+// A multiplier for the default gold cost (change to 0.0f for no default cost)
+const float TransmogDisplayVendorMgr::ScaledCostModifier = 1.0f;
+// Cost added on top of other costs (can be negative)
+const int32 TransmogDisplayVendorMgr::CopperCost = 0;
+// For custom gold cost set ScaledCostModifier to 0.0f and CopperCost to what ever cost you want
+
+const bool TransmogDisplayVendorMgr::RequireToken = false;
+const uint32 TransmogDisplayVendorMgr::TokenEntry = 49426;
+const uint32 TransmogDisplayVendorMgr::TokenAmount = 1;
+
+const bool TransmogDisplayVendorMgr::AllowPoor = false;
+const bool TransmogDisplayVendorMgr::AllowCommon = false;
+const bool TransmogDisplayVendorMgr::AllowUncommon = true;
+const bool TransmogDisplayVendorMgr::AllowRare = true;
+const bool TransmogDisplayVendorMgr::AllowEpic = true;
+const bool TransmogDisplayVendorMgr::AllowLegendary = false;
+const bool TransmogDisplayVendorMgr::AllowArtifact = false;
+const bool TransmogDisplayVendorMgr::AllowHeirloom = true;
+
+const bool TransmogDisplayVendorMgr::AllowMixedArmorTypes = false;
+const bool TransmogDisplayVendorMgr::AllowMixedWeaponTypes = false;
+const bool TransmogDisplayVendorMgr::AllowMixedInventoryTypes = false;
+const bool TransmogDisplayVendorMgr::AllowFishingPoles = false;
+
+const bool TransmogDisplayVendorMgr::IgnoreReqRace = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqClass = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqSkill = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqSpell = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqLevel = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqEvent = false;
+const bool TransmogDisplayVendorMgr::IgnoreReqStats = false;
+
+// Example AllowedItems[] = { 123, 234, 345 };
+const std::set<uint32> TransmogDisplayVendorMgr::AllowedItems = { };
+const std::set<uint32> TransmogDisplayVendorMgr::NotAllowedItems = { };
+
+// Config end
+
+std::vector<uint32> TransmogDisplayVendorMgr::Allowed;
+std::vector<uint32> TransmogDisplayVendorMgr::NotAllowed;
+SelectionStore TransmogDisplayVendorMgr::selectionStore;
+TransmogDisplayVendorMgr::EntryVector* TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR][MAX_INVTYPE][MAX_ITEM_QUALITY] = { };
+
+uint32 TransmogDisplayVendorMgr::GetFakeEntry(const Item* item)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetFakeEntry");
+
+ Player* owner = item->GetOwner();
+
+ if (!owner)
+ return 0;
+ if (owner->transmogMap.empty())
+ return 0;
+
+ TransmogMapType::const_iterator it = owner->transmogMap.find(item->GetGUID());
+ if (it == owner->transmogMap.end())
+ return 0;
+ return it->second;
+}
+void TransmogDisplayVendorMgr::DeleteFakeEntry(Player* player, Item* item)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::DeleteFakeEntry");
+
+ if (player->transmogMap.erase(item->GetGUID()) != 0)
+ UpdateItem(player, item);
+}
+void TransmogDisplayVendorMgr::SetFakeEntry(Player* player, Item* item, uint32 entry)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SetFakeEntry");
+
+ player->transmogMap[item->GetGUID()] = entry;
+ UpdateItem(player, item);
+}
+void TransmogDisplayVendorMgr::UpdateItem(Player* player, Item* item)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::UpdateItem");
+
+ if (item->IsEquipped())
+ {
+ player->SetVisibleItemSlot(item->GetSlot(), item);
+ if (player->IsInWorld())
+ item->SendUpdateToPlayer(player);
+ }
+}
+const char* TransmogDisplayVendorMgr::getSlotName(uint8 slot, WorldSession* /*session*/)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::TransmogDisplayVendorMgr::getSlotName");
+
+ switch (slot)
+ {
+ case EQUIPMENT_SLOT_HEAD: return "Head";// session->GetTrinityString(LANG_SLOT_NAME_HEAD);
+ case EQUIPMENT_SLOT_SHOULDERS: return "Shoulders";// session->GetTrinityString(LANG_SLOT_NAME_SHOULDERS);
+ case EQUIPMENT_SLOT_BODY: return "Shirt";// session->GetTrinityString(LANG_SLOT_NAME_BODY);
+ case EQUIPMENT_SLOT_CHEST: return "Chest";// session->GetTrinityString(LANG_SLOT_NAME_CHEST);
+ case EQUIPMENT_SLOT_WAIST: return "Waist";// session->GetTrinityString(LANG_SLOT_NAME_WAIST);
+ case EQUIPMENT_SLOT_LEGS: return "Legs";// session->GetTrinityString(LANG_SLOT_NAME_LEGS);
+ case EQUIPMENT_SLOT_FEET: return "Feet";// session->GetTrinityString(LANG_SLOT_NAME_FEET);
+ case EQUIPMENT_SLOT_WRISTS: return "Wrists";// session->GetTrinityString(LANG_SLOT_NAME_WRISTS);
+ case EQUIPMENT_SLOT_HANDS: return "Hands";// session->GetTrinityString(LANG_SLOT_NAME_HANDS);
+ case EQUIPMENT_SLOT_BACK: return "Back";// session->GetTrinityString(LANG_SLOT_NAME_BACK);
+ case EQUIPMENT_SLOT_MAINHAND: return "Main hand";// session->GetTrinityString(LANG_SLOT_NAME_MAINHAND);
+ case EQUIPMENT_SLOT_OFFHAND: return "Off hand";// session->GetTrinityString(LANG_SLOT_NAME_OFFHAND);
+ case EQUIPMENT_SLOT_RANGED: return "Ranged";// session->GetTrinityString(LANG_SLOT_NAME_RANGED);
+ case EQUIPMENT_SLOT_TABARD: return "Tabard";// session->GetTrinityString(LANG_SLOT_NAME_TABARD);
+ default: return nullptr;
+ }
+}
+uint32 TransmogDisplayVendorMgr::GetSpecialPrice(ItemTemplate const* proto)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetSpecialPrice");
+
+ uint32 cost = proto->SellPrice < 10000 ? 10000 : proto->SellPrice;
+ return cost;
+}
+bool TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem");
+
+ if (!target || !source)
+ return false;
+
+ if (source->ItemId == target->ItemId)
+ return false;
+
+ if (source->DisplayInfoID == target->DisplayInfoID)
+ return false;
+
+ if (source->Class != target->Class)
+ return false;
+
+ if (source->InventoryType == INVTYPE_BAG ||
+ source->InventoryType == INVTYPE_RELIC ||
+ // source->InventoryType == INVTYPE_BODY ||
+ source->InventoryType == INVTYPE_FINGER ||
+ source->InventoryType == INVTYPE_TRINKET ||
+ source->InventoryType == INVTYPE_AMMO ||
+ source->InventoryType == INVTYPE_QUIVER)
+ return false;
+
+ if (target->InventoryType == INVTYPE_BAG ||
+ target->InventoryType == INVTYPE_RELIC ||
+ // target->InventoryType == INVTYPE_BODY ||
+ target->InventoryType == INVTYPE_FINGER ||
+ target->InventoryType == INVTYPE_TRINKET ||
+ target->InventoryType == INVTYPE_AMMO ||
+ target->InventoryType == INVTYPE_QUIVER)
+ return false;
+
+ if (!SuitableForTransmogrification(player, target) || !SuitableForTransmogrification(player, source)) // if (!transmogrified->CanTransmogrify() || !transmogrifier->CanBeTransmogrified())
+ return false;
+
+ if (IsRangedWeapon(source->Class, source->SubClass) != IsRangedWeapon(target->Class, target->SubClass))
+ return false;
+
+ if (source->SubClass != target->SubClass && !IsRangedWeapon(target->Class, target->SubClass))
+ {
+ if (source->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
+ return false;
+ if (source->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
+ return false;
+ }
+
+ if (source->InventoryType != target->InventoryType)
+ {
+ if (source->Class == ITEM_CLASS_WEAPON && !((IsRangedWeapon(target->Class, target->SubClass) ||
+ AllowMixedInventoryTypes ||
+ ((target->InventoryType == INVTYPE_WEAPON || target->InventoryType == INVTYPE_2HWEAPON) &&
+ (source->InventoryType == INVTYPE_WEAPON || source->InventoryType == INVTYPE_2HWEAPON)) ||
+ ((target->InventoryType == INVTYPE_WEAPONMAINHAND || target->InventoryType == INVTYPE_WEAPONOFFHAND) &&
+ (source->InventoryType == INVTYPE_WEAPON || source->InventoryType == INVTYPE_2HWEAPON)))))
+ return false;
+ if (source->Class == ITEM_CLASS_ARMOR &&
+ !((source->InventoryType == INVTYPE_CHEST || source->InventoryType == INVTYPE_ROBE) &&
+ (target->InventoryType == INVTYPE_CHEST || target->InventoryType == INVTYPE_ROBE)))
+ return false;
+ }
+
+ return true;
+}
+bool TransmogDisplayVendorMgr::SuitableForTransmogrification(Player* player, ItemTemplate const* proto)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SuitableForTransmogrification");
+
+ // ItemTemplate const* proto = item->GetTemplate();
+ if (!proto)
+ return false;
+
+ if (proto->Class != ITEM_CLASS_ARMOR &&
+ proto->Class != ITEM_CLASS_WEAPON)
+ return false;
+
+ // Skip all checks for allowed items
+ if (IsAllowed(proto->ItemId))
+ return true;
+
+ if (IsNotAllowed(proto->ItemId))
+ return false;
+
+ if (!AllowFishingPoles && proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
+ return false;
+
+ if (!IsAllowedQuality(proto->Quality)) // (proto->Quality == ITEM_QUALITY_LEGENDARY)
+ return false;
+
+ if (player)
+ {
+ if ((proto->Flags2 & ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE)
+ return false;
+
+ if ((proto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE)
+ return false;
+
+ if (!IgnoreReqClass && (proto->AllowableClass & player->GetClassMask()) == 0)
+ return false;
+
+ if (!IgnoreReqRace && (proto->AllowableRace & player->GetRaceMask()) == 0)
+ return false;
+
+ if (!IgnoreReqSkill && proto->RequiredSkill != 0)
+ {
+ if (player->GetSkillValue(proto->RequiredSkill) == 0)
+ return false;
+ else if (player->GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
+ return false;
+ }
+
+ if (!IgnoreReqSpell && proto->RequiredSpell != 0 && !player->HasSpell(proto->RequiredSpell))
+ return false;
+
+ if (!IgnoreReqLevel && player->GetLevel() < proto->RequiredLevel)
+ return false;
+ }
+
+ // If World Event is not active, prevent using event dependant items
+ if (!IgnoreReqEvent && proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
+ return false;
+
+ if (!IgnoreReqStats)
+ {
+ if (!proto->RandomProperty && !proto->RandomSuffix)
+ {
+ bool found = false;
+ for (uint8 i = 0; i < proto->StatsCount; ++i)
+ {
+ if (proto->ItemStat[i].ItemStatValue != 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TransmogDisplayVendorMgr::IsRangedWeapon(uint32 Class, uint32 SubClass)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsRangedWeapon");
+
+ return Class == ITEM_CLASS_WEAPON && (
+ SubClass == ITEM_SUBCLASS_WEAPON_BOW ||
+ SubClass == ITEM_SUBCLASS_WEAPON_GUN ||
+ SubClass == ITEM_SUBCLASS_WEAPON_CROSSBOW);
+}
+bool TransmogDisplayVendorMgr::IsAllowed(uint32 entry)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowed");
+
+ return std::find(Allowed.begin(), Allowed.end(), entry) != Allowed.end();
+}
+bool TransmogDisplayVendorMgr::IsNotAllowed(uint32 entry)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsNotAllowed");
+
+ return std::find(NotAllowed.begin(), NotAllowed.end(), entry) != NotAllowed.end();
+}
+bool TransmogDisplayVendorMgr::IsAllowedQuality(uint32 quality)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowedQuality");
+
+ switch (quality)
+ {
+ case ITEM_QUALITY_POOR: return AllowPoor;
+ case ITEM_QUALITY_NORMAL: return AllowCommon;
+ case ITEM_QUALITY_UNCOMMON: return AllowUncommon;
+ case ITEM_QUALITY_RARE: return AllowRare;
+ case ITEM_QUALITY_EPIC: return AllowEpic;
+ case ITEM_QUALITY_LEGENDARY: return AllowLegendary;
+ case ITEM_QUALITY_ARTIFACT: return AllowArtifact;
+ case ITEM_QUALITY_HEIRLOOM: return AllowHeirloom;
+ default: return false;
+ }
+}
+
+void TransmogDisplayVendorMgr::HandleTransmogrify(Player* player, Creature* /*creature*/, uint32 vendorslot, uint32 itemEntry, bool no_cost)
+{
+ TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify");
+
+ SelectionStore::Selection selection;
+ if (!selectionStore.GetSelection(player->GetGUID().GetCounter(), selection))
+ return; // cheat, no slot selected
+
+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(selection.slot, player->GetSession());
+ if (!slotname)
+ return;
+ uint8 slot = selection.slot;
+
+ // slot of the transmogrified item
+ if (slot >= EQUIPMENT_SLOT_END)
+ {
+ 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);
+ return; // LANG_ERR_TRANSMOG_INVALID_SLOT
+ }
+
+ const ItemTemplate* itemTransmogrifier = nullptr;
+ // guid of the transmogrifier item, if it's not 0
+ if (itemEntry)
+ {
+ itemTransmogrifier = sObjectMgr->GetItemTemplate(itemEntry);
+ if (!itemTransmogrifier)
+ {
+ 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);
+ return; // LANG_ERR_TRANSMOG_MISSING_SRC_ITEM
+ }
+ }
+
+ // transmogrified item
+ Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+ if (!itemTransmogrified)
+ {
+ 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);
+ player->GetSession()->SendNotification("No item in %s slot", slotname);
+ return; // LANG_ERR_TRANSMOG_MISSING_DEST_ITEM
+ }
+
+ if (!itemTransmogrifier) // reset look newEntry
+ {
+ DeleteFakeEntry(player, itemTransmogrified);
+ }
+ else
+ {
+ if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier))
+ {
+ 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);
+ player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
+ return; // LANG_ERR_TRANSMOG_INVALID_ITEMS
+ }
+
+ if (uint32 fakeEntry = GetFakeEntry(itemTransmogrified))
+ {
+ if (const ItemTemplate* fakeItemTemplate = sObjectMgr->GetItemTemplate(fakeEntry))
+ {
+ if (fakeItemTemplate->DisplayInfoID == itemTransmogrifier->DisplayInfoID)
+ {
+ player->GetSession()->SendNotification("%s already transmogrified with %s", slotname, getItemName(itemTransmogrifier, player->GetSession()).c_str());
+ return;
+ }
+ }
+ }
+
+ // {{entry}, {entry}, ...}
+ std::list<uint32> L;
+ uint32 counter = 0;
+ bool over = false;
+ if (itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ {
+ const EntryVector* oM = optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
+ if (!oM)
+ continue;
+ if (!over && counter + oM->size() < selection.offset)
+ {
+ counter += oM->size();
+ }
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+ else if (itemTransmogrified->GetTemplate()->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
+ {
+ const EntryVector* oM = optionMap[i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
+ if (!oM)
+ continue;
+ if (!over && counter + oM->size() < selection.offset)
+ {
+ counter += oM->size();
+ }
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+ else
+ {
+ const EntryVector* oM = optionMap[(itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTransmogrified->GetTemplate()->SubClass][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
+ if (oM)
+ {
+ if (!over && counter + oM->size() < selection.offset)
+ {
+ counter += oM->size();
+ }
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+ std::list<uint32>::const_iterator it = L.begin();
+ std::advance(it, (selection.offset - counter) + vendorslot);
+ if (it == L.end() || (*it) != itemEntry)
+ {
+ player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
+ return; // either cheat or changed items (not found in correct place in transmog vendor view)
+ }
+
+ if (!no_cost)
+ {
+ if (RequireToken)
+ {
+ if (player->HasItemCount(TokenEntry, TokenAmount))
+ {
+ player->DestroyItemCount(TokenEntry, TokenAmount, true);
+ }
+ else
+ {
+ player->GetSession()->SendNotification("You do not have enough %ss", getItemName(sObjectMgr->GetItemTemplate(TransmogDisplayVendorMgr::TokenEntry), player->GetSession()).c_str());
+ return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS
+ }
+ }
+
+ int32 cost = 0;
+ cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
+ cost *= ScaledCostModifier;
+ cost += CopperCost;
+
+ if (cost) // 0 cost if reverting look
+ {
+ if (cost < 0)
+ {
+ 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);
+ }
+ else
+ {
+ if (!player->HasEnoughMoney(cost))
+ {
+ player->GetSession()->SendNotification("You do not have enough money");
+ return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY
+ }
+ player->ModifyMoney(-cost, false);
+ }
+ }
+
+ SetFakeEntry(player, itemTransmogrified, itemTransmogrifier->ItemId);
+
+ itemTransmogrified->UpdatePlayedTime(player);
+
+ itemTransmogrified->SetOwnerGUID(player->GetGUID());
+ itemTransmogrified->SetNotRefundable(player);
+ itemTransmogrified->ClearSoulboundTradeable(player);
+
+ //if (itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_USE)
+ // itemTransmogrifier->SetBinding(true);
+
+ //itemTransmogrifier->SetOwnerGUID(player->GetGUID());
+ //itemTransmogrifier->SetNotRefundable(player);
+ //itemTransmogrifier->ClearSoulboundTradeable(player);
+ }
+
+ player->GetSession()->SendAreaTriggerMessage("%s transmogrified", slotname);
+ //return LANG_ERR_TRANSMOG_OK;
+ }
+}
+
+const char * TransmogDisplayVendorMgr::getQualityName(uint32 quality)
+{
+ switch (quality)
+ {
+ case ITEM_QUALITY_POOR: return "|CFF9d9d9d[Poor]";
+ case ITEM_QUALITY_NORMAL: return "|CFFffffff[Common]";
+ case ITEM_QUALITY_UNCOMMON: return "|CFF1eff00[Uncommon]";
+ case ITEM_QUALITY_RARE: return "|CFF0070dd[Rare]";
+ case ITEM_QUALITY_EPIC: return "|CFFa335ee[Epic]";
+ case ITEM_QUALITY_LEGENDARY: return "|CFFff8000[Legendary]";
+ case ITEM_QUALITY_ARTIFACT: return "|CFFe6cc80[Artifact]";
+ case ITEM_QUALITY_HEIRLOOM: return "|CFFe5cc80[Heirloom]";
+ default: return "[Unknown]";
+ }
+}
+
+std::string TransmogDisplayVendorMgr::getItemName(const ItemTemplate * itemTemplate, WorldSession * session)
+{
+ std::string name = itemTemplate->Name1;
+ LocaleConstant loc_idx = session->GetSessionDbLocaleIndex();
+ if (loc_idx != LOCALE_enUS)
+ if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
+ sObjectMgr->GetLocaleString(il->Name, loc_idx, name);
+ return name;
+}
+
+uint32 TransmogDisplayVendorMgr::getCorrectInvType(uint32 inventorytype)
+{
+ switch (inventorytype)
+ {
+ case INVTYPE_2HWEAPON:
+ if (!AllowMixedInventoryTypes)
+ break;
+ case INVTYPE_WEAPONMAINHAND:
+ case INVTYPE_WEAPONOFFHAND:
+ return INVTYPE_WEAPON;
+ case INVTYPE_RANGEDRIGHT:
+ return INVTYPE_RANGED;
+ case INVTYPE_ROBE:
+ return INVTYPE_CHEST;
+ default:
+ break;
+ }
+ return inventorytype;
+}
diff --git a/src/server/game/Entities/Item/TransmogDisplayVendorConf.h b/src/server/game/Entities/Item/TransmogDisplayVendorConf.h
new file mode 100644
index 000000000000..09ece53001db
--- /dev/null
+++ b/src/server/game/Entities/Item/TransmogDisplayVendorConf.h
@@ -0,0 +1,150 @@
+#ifndef DEF_TRANSMOGRIFICATION_DISPLAY_H
+#define DEF_TRANSMOGRIFICATION_DISPLAY_H
+
+/*
+Transmogrification display vendor
+Code by Rochet2
+Ideas LilleCarl
+
+ScriptName for NPC:
+NPC_TransmogDisplayVendor
+
+Compatible with Transmogrification 6.1 by Rochet2
+http://rochet2.github.io/Transmogrification
+*/
+
+// use 0 or 1
+#define TRANSMOGRIFICATION_ALREADY_INSTALLED 0
+// Note! If you use both, set this to true (1) and in scriptloader make transmog load first
+
+#include "Define.h"
+#include "ItemTemplate.h"
+#include "SharedDefines.h"
+#include <set>
+#include <vector>
+#include <unordered_map>
+#include <mutex>
+
+class Creature;
+class Item;
+class Player;
+class WorldSession;
+struct ItemTemplate;
+
+enum TransmogDisplayVendorSenders
+{
+ SENDER_START = MAX_ITEM_QUALITY,
+ SENDER_BACK,
+ SENDER_SELECT_VENDOR,
+ SENDER_REMOVE_ALL,
+ SENDER_REMOVE_ONE,
+ SENDER_REMOVE_MENU,
+ SENDER_END,
+};
+
+class TC_GAME_API SelectionStore
+{
+public:
+ typedef std::mutex LockType;
+ typedef std::unique_lock<LockType> Guard;
+
+ struct Selection { uint32 item; uint8 slot; uint32 offset; uint32 quality; };
+ typedef std::unordered_map<uint32, Selection> PlayerLowToSelection;
+
+ void SetSelection(uint32 playerLow, const Selection& selection)
+ {
+ Guard guard(_lock);
+ hashmap[playerLow] = selection;
+ }
+
+ bool GetSelection(uint32 playerLow, Selection& returnVal)
+ {
+ Guard guard(_lock);
+
+ PlayerLowToSelection::iterator it = hashmap.find(playerLow);
+ if (it == hashmap.end())
+ return false;
+
+ returnVal = it->second;
+ return true;
+ }
+
+ void RemoveSelection(uint32 playerLow)
+ {
+ Guard guard(_lock);
+ hashmap.erase(playerLow);
+ }
+
+private:
+ PlayerLowToSelection hashmap;
+ LockType _lock;
+};
+
+class TC_GAME_API TransmogDisplayVendorMgr
+{
+public:
+ // Selection store
+ static SelectionStore selectionStore; // selectionStore[lowGUID] = Selection
+
+ // Vendor data store
+ // optionMap[Class? + SubClass][invtype][Quality] = EntryVector
+ typedef std::vector<uint32> EntryVector;
+ static EntryVector* optionMap[MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR][MAX_INVTYPE][MAX_ITEM_QUALITY];
+
+ static const std::set<uint32> AllowedItems;
+ static const std::set<uint32> NotAllowedItems;
+
+ static const float ScaledCostModifier;
+ static const int32 CopperCost;
+
+ static const bool RequireToken;
+ static const uint32 TokenEntry;
+ static const uint32 TokenAmount;
+
+ static const bool AllowPoor;
+ static const bool AllowCommon;
+ static const bool AllowUncommon;
+ static const bool AllowRare;
+ static const bool AllowEpic;
+ static const bool AllowLegendary;
+ static const bool AllowArtifact;
+ static const bool AllowHeirloom;
+
+ static const bool AllowMixedArmorTypes;
+ static const bool AllowMixedWeaponTypes;
+ static const bool AllowMixedInventoryTypes;
+ static const bool AllowFishingPoles;
+
+ static const bool IgnoreReqRace;
+ static const bool IgnoreReqClass;
+ static const bool IgnoreReqSkill;
+ static const bool IgnoreReqSpell;
+ static const bool IgnoreReqLevel;
+ static const bool IgnoreReqEvent;
+ static const bool IgnoreReqStats;
+
+ static std::vector<uint32> Allowed;
+ static std::vector<uint32> NotAllowed;
+
+ static void HandleTransmogrify(Player* player, Creature* creature, uint32 vendorslot, uint32 itemEntry, bool no_cost = false);
+
+ static const char* getQualityName(uint32 quality);
+ static std::string getItemName(const ItemTemplate* itemTemplate, WorldSession* session);
+ static uint32 getCorrectInvType(uint32 inventorytype);
+
+ // From Transmogrification
+ static uint32 GetFakeEntry(const Item* item);
+ static void DeleteFakeEntry(Player* player, Item* item);
+ static void SetFakeEntry(Player* player, Item* item, uint32 entry);
+ static const char* getSlotName(uint8 slot, WorldSession* session);
+ static void UpdateItem(Player* player, Item* item);
+ static uint32 GetSpecialPrice(ItemTemplate const* proto);
+ static bool CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source);
+ static bool SuitableForTransmogrification(Player* player, ItemTemplate const* proto);
+ static bool IsRangedWeapon(uint32 Class, uint32 SubClass);
+ static bool IsAllowed(uint32 entry);
+ static bool IsNotAllowed(uint32 entry);
+ static bool IsAllowedQuality(uint32 quality);
+};
+
+#endif
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 8c3fcafaf83a..5b00d2ce3134 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "TransmogDisplayVendorConf.h"
#include "Player.h"
#include "AccountMgr.h"
#include "AchievementMgr.h"
@@ -12244,7 +12245,10 @@ void Player::SetVisibleItemSlot(uint8 slot, Item* pItem)
{
if (pItem)
{
- SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry());
+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(pItem))
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), entry);
+ else
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry());
SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT));
}
@@ -12375,6 +12379,7 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
if (Item* it = GetItemByPos(bag, slot))
{
RemoveItem(bag, slot, update);
+ TransmogDisplayVendorMgr::DeleteFakeEntry(this, it);
ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
it->SetNotRefundable(this, false);
RemoveItemFromUpdateQueueOf(it, this);
@@ -21708,24 +21713,30 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
return false;
}
- if (!(pProto->AllowableClass & GetClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
+ Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
+ if (!creature)
{
- SendBuyError(BUY_ERR_CANT_FIND_ITEM, nullptr, item, 0);
+ TC_LOG_DEBUG("network", "Player::BuyItemFromVendorSlot: Vendor (%s) not found or player '%s' (%s) can't interact with him.",
+ vendorguid.ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str());
+ SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, nullptr, item, 0);
return false;
}
- if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAG2_FACTION_HORDE && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAG2_FACTION_ALLIANCE && GetTeam() == HORDE)))
+ if (creature->GetScriptName() == "NPC_TransmogDisplayVendor")
+ {
+ TransmogDisplayVendorMgr::HandleTransmogrify(this, creature, vendorslot, item);
return false;
+ }
- Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
- if (!creature)
+ if (!(pProto->AllowableClass & GetClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
{
- TC_LOG_DEBUG("network", "Player::BuyItemFromVendorSlot: Vendor (%s) not found or player '%s' (%s) can't interact with him.",
- vendorguid.ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str());
- SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, nullptr, item, 0);
+ SendBuyError(BUY_ERR_CANT_FIND_ITEM, nullptr, item, 0);
return false;
}
+ if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAG2_FACTION_HORDE && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAG2_FACTION_ALLIANCE && GetTeam() == HORDE)))
+ return false;
+
if (!sConditionMgr->IsObjectMeetingVendorItemConditions(creature->GetEntry(), item, this, creature))
{
TC_LOG_DEBUG("condition", "Player::BuyItemFromVendorSlot: Player '%s' (%s) doesn't meed conditions for creature (Entry: %u, Item: %u)",
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index b5ddaca836c5..ba5ece397ff5 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -151,6 +151,18 @@ struct SpellModifier
Aura* const ownerAura;
};
+typedef std::unordered_map<ObjectGuid, uint32> TransmogMapType;
+
+#ifdef PRESETS
+typedef std::map<uint8, uint32> PresetslotMapType;
+struct PresetData
+{
+ std::string name;
+ PresetslotMapType slotMap; // slotMap[slotId] = entry
+};
+typedef std::map<uint8, PresetData> PresetMapType;
+#endif
+
typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
typedef std::unordered_map<uint32, PlayerSpell> PlayerSpellMap;
typedef std::unordered_set<SpellModifier*> SpellModContainer;
@@ -2180,6 +2192,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
std::string GetMapAreaAndZoneString() const;
std::string GetCoordsMapAreaAndZoneString() const;
+ TransmogMapType transmogMap; // transmogMap[iGUID] = entry
+#ifdef PRESETS
+ PresetMapType presetMap; // presetMap[presetId] = presetData
+#endif
+
std::string GetDebugInfo() const override;
protected:
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 3991676dca9c..333fd2eca847 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "TransmogDisplayVendorConf.h"
#include "WorldSession.h"
#include "Common.h"
#include "Config.h"
@@ -679,7 +680,12 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
data << uint32(0);
else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
- data << uint32(item->GetTemplate()->DisplayInfoID);
+ {
+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(item))
+ data << uint32(sObjectMgr->GetItemTemplate(entry)->DisplayInfoID);
+ else
+ data << uint32(item->GetTemplate()->DisplayInfoID);
+ }
else
data << uint32(0);
}
diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/README.md b/src/server/scripts/Custom/TransmogDisplayVendor/README.md
new file mode 100644
index 000000000000..93807c92e7ec
--- /dev/null
+++ b/src/server/scripts/Custom/TransmogDisplayVendor/README.md
@@ -0,0 +1,50 @@
+# Transmogrification Display Vendor [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=transmogvendor_3.3.5)](https://travis-ci.org/Rochet2/TrinityCore)
+
+#### About
+Original idea by LilleCarl.
+Coding work and execution by Rochet2.
+Transmogrification Display Vendor allows you to change the display of an item to something else.
+You can use any item display in the game, as long as it fits the requirements.
+Requirements can be tweaked in the `TransmogDisplayVendor.cpp` file.
+Basically any item should work with transmogrification. Custom items as well. No item is hardcoded to the system.
+Has a feature to work with the regular [Transmogrification](http://rochet2.github.io/Transmogrification.html). This can be enabled before compiling in `TransmogDisplayVendor.h`.
+Made for 3.3.5a.<br />
+Source: http://www.trinitycore.org/f/topic/7993-transmogrification-display-vendor/
+
+Video: https://youtu.be/PIheEziN_dY
+
+#### Installation
+
+Available as:
+- Direct merge: https://github.com/Rochet2/TrinityCore/tree/transmogvendor_3.3.5
+- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...transmogvendor_3.3.5.diff
+- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...transmogvendor_3.3.5
+
+Using direct merge:
+- open git bash to source location
+- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git`
+- do `git pull rochet2 transmogvendor_3.3.5`
+- optionally tweak the config settings in `src/server/game/Entities/Item/TransmogDisplayVendor.cpp`
+- use cmake and compile
+
+Using diff *(recommended)*:
+- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail.
+- download the diff by __right clicking__ the link and select __Save link as__
+- place the downloaded `transmogvendor_3.3.5.diff` to the source root folder
+- open git bash to source location
+- do `git apply transmogvendor_3.3.5.diff`
+ - 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.
+- optionally tweak the config settings in `src/server/game/Entities/Item/TransmogDisplayVendor.cpp`
+- use cmake and compile
+
+After compiling:
+- TrinityCore auto updater should run needed SQLs automatically.
+- If you do not use the auto updater then run files named `*_transmogvendor.sql` from `\sql\custom` to your databases.
+- Optionally you can also insert a transmogrifier NPC to your database by running `\src\server\scripts\Custom\TransmogDisplayVendor\sql\world_NPC.sql` to your world database.
+
+#### Usage
+Equip an item that is suitable for transmogrification.
+Talk to Transmogrifier and select the item slot. Then select the quality and then the item you want to transmogrify to.
+
+#### Bugs and Contact
+Report issues and similar to http://rochet2.github.io/
diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp b/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp
new file mode 100644
index 000000000000..e7ca0a870c0d
--- /dev/null
+++ b/src/server/scripts/Custom/TransmogDisplayVendor/TransmogVendor.cpp
@@ -0,0 +1,575 @@
+/*
+Transmog display vendor
+Code by Rochet2
+Ideas LilleCarl
+
+ScriptName for NPC:
+NPC_TransmogDisplayVendor
+
+Compatible with Transmogrification 6.1 by Rochet2
+http://rochet2.github.io/Transmogrification
+*/
+
+#include "TransmogDisplayVendorConf.h"
+#include "Bag.h"
+#include "Common.h"
+#include "Config.h"
+#include "Creature.h"
+#include "DatabaseEnv.h"
+#include "DBCStructure.h"
+#include "Define.h"
+#include "Field.h"
+#include "GameEventMgr.h"
+#include "GossipDef.h"
+#include "Item.h"
+#include "ItemTemplate.h"
+#include "Language.h"
+#include "Log.h"
+#include "Player.h"
+#include "ObjectGuid.h"
+#include "ObjectMgr.h"
+#include "QueryResult.h"
+#include "ScriptedCreature.h"
+#include "ScriptedGossip.h"
+#include "ScriptMgr.h"
+#include "SharedDefines.h"
+#include "Transaction.h"
+#include "WorldSession.h"
+#include <sstream>
+#include <string>
+
+class NPC_TransmogDisplayVendor : public CreatureScript
+{
+public:
+ NPC_TransmogDisplayVendor() : CreatureScript("NPC_TransmogDisplayVendor") { } // If you change this, also change in Player.cpp: if (creature->GetScriptName() == "NPC_TransmogDisplayVendor")
+
+ class TransmogVendorAI : public ScriptedAI
+ {
+ public:
+ TransmogVendorAI(Creature* creature) : ScriptedAI(creature) {}
+
+ bool OnGossipHello(Player* player) override
+ {
+ return OnGossipHello(player, me);
+ }
+
+ static bool OnGossipHello(Player* player, Creature* creature)
+ {
+ ClearGossipMenuFor(player);
+ TransmogDisplayVendorMgr::selectionStore.RemoveSelection(player->GetGUID().GetCounter());
+ WorldSession* session = player->GetSession();
+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
+ {
+ // if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
+ if (const char* slotName = TransmogDisplayVendorMgr::getSlotName(slot, session))
+ AddGossipItemFor(player, GOSSIP_ICON_TRAINER, slotName, SENDER_SELECT_VENDOR, slot);
+ }
+ AddGossipItemFor(player, GOSSIP_ICON_TRAINER, "Remove transmogrifications", SENDER_REMOVE_MENU, 0);
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
+ return true;
+ }
+
+ bool OnGossipSelect(Player* player, uint32 /*menu_id*/, uint32 gossipListId) override
+ {
+ uint32 sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId);
+ uint32 action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId);
+ return OnGossipSelect(player, me, sender, action);
+ }
+
+ static bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
+ {
+ WorldSession* session = player->GetSession();
+ ClearGossipMenuFor(player);
+ switch (sender)
+ {
+ case SENDER_SELECT_VENDOR: // action = slot
+ {
+ Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action);
+ if (!item)
+ {
+ if (const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession()))
+ session->SendNotification("No item equipped in %s slot", slotname);
+ OnGossipHello(player, creature);
+ return true;
+ }
+ const ItemTemplate * itemTemplate = item->GetTemplate();
+ AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, (std::string)"Update selected; " + TransmogDisplayVendorMgr::getItemName(itemTemplate, session), sender, action);
+
+ // [quality] = {size}
+ std::map<uint32, uint32> L;
+ if (itemTemplate->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ {
+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
+ if (*oM)
+ L[i] += (*oM)->size();
+ }
+ }
+ else if (itemTemplate->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
+ {
+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
+ if (*oM)
+ L[i] += (*oM)->size();
+ }
+ }
+ else
+ {
+ TransmogDisplayVendorMgr::EntryVector** oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)];
+ for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i, ++oM)
+ if (TransmogDisplayVendorMgr::IsAllowedQuality(i)) // skip not allowed qualities
+ if (*oM)
+ L[i] += (*oM)->size();
+ }
+
+ for (std::map<uint32, uint32>::const_iterator it = L.begin(); it != L.end(); ++it)
+ {
+ for (uint32 count = 0; count*MAX_VENDOR_ITEMS < it->second; ++count)
+ {
+ std::ostringstream ss;
+ ss << TransmogDisplayVendorMgr::getQualityName(it->first);
+ if (count)
+ ss << " [" << count << "]";
+ AddGossipItemFor(player, GOSSIP_ICON_VENDOR, ss.str().c_str(), it->first, count*MAX_VENDOR_ITEMS);
+ }
+ }
+
+ if (player->PlayerTalkClass->GetGossipMenu().GetMenuItemCount() <= 1)
+ {
+ if (const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession()))
+ session->SendNotification("No transmogrifications available for %s", slotname);
+ OnGossipHello(player, creature);
+ return true;
+ }
+
+ SelectionStore::Selection temp = { item->GetEntry(), static_cast<uint8>(action), 0, 0 }; // entry, slot, offset, quality
+ TransmogDisplayVendorMgr::selectionStore.SetSelection(player->GetGUID().GetCounter(), temp);
+ AddGossipItemFor(player, GOSSIP_ICON_TALK, "Back..", SENDER_BACK, 0);
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
+ } break;
+ case SENDER_BACK: // Back
+ {
+ OnGossipHello(player, creature);
+ } break;
+ case SENDER_REMOVE_ALL: // Remove TransmogDisplayVendorMgrs
+ {
+ bool removed = false;
+ for (uint8 Slot = EQUIPMENT_SLOT_START; Slot < EQUIPMENT_SLOT_END; Slot++)
+ {
+ if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, Slot))
+ {
+ if (!TransmogDisplayVendorMgr::GetFakeEntry(newItem))
+ continue;
+ TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
+ removed = true;
+ }
+ }
+ if (removed)
+ {
+ session->SendAreaTriggerMessage("Transmogrifications removed from equipped items");
+ }
+ else
+ {
+ session->SendNotification("You have no transmogrified items equipped");
+ }
+ OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
+ } break;
+ case SENDER_REMOVE_ONE: // Remove TransmogDisplayVendorMgr from single item
+ {
+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession());
+ if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
+ {
+ if (TransmogDisplayVendorMgr::GetFakeEntry(newItem))
+ {
+ TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
+ if (slotname)
+ session->SendAreaTriggerMessage("%s transmogrification removed", slotname);
+ }
+ else if (slotname)
+ {
+ session->SendNotification("No transmogrification on %s slot", slotname);
+ }
+ }
+ else if (slotname)
+ {
+ session->SendNotification("No item equipped in %s slot", slotname);
+ }
+ OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
+ } break;
+ case SENDER_REMOVE_MENU:
+ {
+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
+ {
+ const char* slotname = TransmogDisplayVendorMgr::getSlotName(slot, player->GetSession());
+ if (!slotname)
+ continue;
+ std::ostringstream ss;
+ ss << "Remove transmogrification from " << slotname << "?";
+ AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, (std::string)"Remove from " + slotname, SENDER_REMOVE_ONE, slot, ss.str().c_str(), 0, false);
+ }
+ AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, "Remove all transmogrifications", SENDER_REMOVE_ALL, 0, "Are you sure you want to remove all transmogrifications?", 0, false);
+ AddGossipItemFor(player, GOSSIP_ICON_TALK, "Back..", SENDER_BACK, 0);
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
+ } break;
+ default: // Show items you can use
+ {
+ if (sender >= MAX_ITEM_QUALITY) // sender = quality, action = iterator
+ return false; // cheat
+
+ SelectionStore::Selection selection;
+ if (!TransmogDisplayVendorMgr::selectionStore.GetSelection(player->GetGUID().GetCounter(), selection))
+ return false; // cheat
+ if (selection.offset != 0 || selection.quality != 0)
+ return false; // cheat (something is off)
+
+ selection.offset = action;
+ selection.quality = sender;
+ uint32 slot = selection.slot; // slot
+ TransmogDisplayVendorMgr::selectionStore.SetSelection(player->GetGUID().GetCounter(), selection);
+
+ if (const ItemTemplate* itemTemplate = sObjectMgr->GetItemTemplate(selection.item))
+ {
+ if (!TransmogDisplayVendorMgr::SuitableForTransmogrification(player, itemTemplate))
+ {
+ player->GetSession()->SendNotification("Equipped item is not suitable for transmogrification");
+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
+ return true;
+ }
+
+ // {{entry}, {entry}, ...}
+ std::list<uint32> L;
+ uint32 counter = 0;
+ bool over = false;
+ if (itemTemplate->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ {
+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
+ if (!oM)
+ continue;
+ if (!over && counter + oM->size() < selection.offset)
+ {
+ counter += oM->size();
+ }
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+ else if (itemTemplate->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
+ {
+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[i][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
+ if (!oM)
+ continue;
+ if (!over && counter + oM->size() < selection.offset)
+ counter += oM->size();
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+ else
+ {
+ const TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
+ if (oM)
+ {
+ if (!over && counter + oM->size() < selection.offset)
+ {
+ counter += oM->size();
+ }
+ else
+ {
+ over = true;
+ L.insert(L.end(), oM->begin(), oM->end());
+ }
+ }
+ }
+
+ // TransmogDisplayVendorMgr::EntryVector oM = TransmogDisplayVendorMgr::optionMap[(itemTemplate->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTemplate->SubClass][TransmogDisplayVendorMgr::getCorrectInvType(itemTemplate->InventoryType)][selection.quality];
+ uint32 itemCount = L.size() - (selection.offset - counter);
+ if (itemCount > MAX_VENDOR_ITEMS)
+ itemCount = MAX_VENDOR_ITEMS;
+
+ if (!itemCount)
+ {
+ session->SendAreaTriggerMessage("No items found");
+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
+ return true;
+ }
+ CloseGossipMenuFor(player);
+
+ TC_LOG_DEBUG("network", "WORLD: Sent SMSG_LIST_INVENTORY");
+
+ Creature* vendor = player->GetNPCIfCanInteractWith(creature->GetGUID(), UNIT_NPC_FLAG_VENDOR);
+ if (!vendor)
+ {
+ TC_LOG_DEBUG("network", "WORLD: SendListInventory - Unit (GUID: %u) not found or you can not interact with him.", creature->GetGUID().GetCounter());
+ player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty, 0);
+ return true;
+ }
+
+ if (player->HasUnitState(UNIT_STATE_DIED))
+ player->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
+ if (vendor->HasUnitState(UNIT_STATE_MOVING))
+ vendor->StopMoving();
+
+ uint8 count = 0;
+
+ WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4);
+ data << uint64(creature->GetGUID());
+
+ size_t countPos = data.wpos();
+ data << uint8(count);
+
+ uint32 item_amount = 0;
+ std::list<uint32>::const_iterator it = L.begin();
+ std::advance(it, (selection.offset - counter));
+ for (; it != L.end() && count < itemCount; ++it, ++count)
+ {
+ if (ItemTemplate const* curtemp = sObjectMgr->GetItemTemplate(*it))
+ {
+ if (!TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(player, itemTemplate, curtemp))
+ continue;
+
+ data << uint32(count + 1);
+ data << uint32(curtemp->ItemId);
+ data << uint32(curtemp->DisplayInfoID);
+ data << int32(0xFFFFFFFF);
+ data << uint32(0);
+ data << uint32(curtemp->MaxDurability);
+ data << uint32(curtemp->BuyCount);
+ data << uint32(0);
+ ++item_amount;
+ }
+ }
+
+ if (!item_amount)
+ {
+ session->SendAreaTriggerMessage("No transmogrifications found for equipped item");
+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
+ return true;
+ }
+ else
+ {
+ data.put<uint8>(countPos, item_amount);
+ session->SendPacket(&data);
+ }
+ }
+ else
+ {
+ session->SendNotification("Invalid item equipped");
+ OnGossipSelect(player, creature, SENDER_SELECT_VENDOR, slot);
+ return true;
+ }
+ } break;
+ }
+ return true;
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new TransmogVendorAI(creature);
+ }
+};
+
+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
+class Player_Transmogrify : public PlayerScript
+{
+public:
+ Player_Transmogrify() : PlayerScript("Player_Transmogrify") { }
+
+ std::vector<ObjectGuid> GetItemList(const Player* player) const
+ {
+ std::vector<ObjectGuid> itemlist;
+
+ // Copy paste from Player::GetItemByGuid(guid)
+
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ itemlist.push_back(pItem->GetGUID());
+
+ for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ itemlist.push_back(pItem->GetGUID());
+
+ for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ itemlist.push_back(pItem->GetGUID());
+
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ if (Bag* pBag = player->GetBagByPos(i))
+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
+ if (Item* pItem = pBag->GetItemByPos(j))
+ itemlist.push_back(pItem->GetGUID());
+
+ for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
+ if (Bag* pBag = player->GetBagByPos(i))
+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
+ if (Item* pItem = pBag->GetItemByPos(j))
+ itemlist.push_back(pItem->GetGUID());
+
+ return itemlist;
+ }
+
+ void OnSave(Player* player) override
+ {
+ uint32 lowguid = player->GetGUID().GetCounter();
+ auto trans = CharacterDatabase.BeginTransaction();
+ trans->PAppend("DELETE FROM `custom_transmogrification` WHERE `Owner` = %u", lowguid);
+
+ if (!player->transmogMap.empty())
+ {
+ // Only save items that are in inventory / bank / etc
+ std::vector<ObjectGuid> items = GetItemList(player);
+ for (std::vector<ObjectGuid>::const_iterator it = items.begin(); it != items.end(); ++it)
+ {
+ TransmogMapType::const_iterator it2 = player->transmogMap.find(*it);
+ if (it2 == player->transmogMap.end())
+ continue;
+
+ trans->PAppend("REPLACE INTO custom_transmogrification (GUID, FakeEntry, Owner) VALUES (%u, %u, %u)", it2->first.GetCounter(), it2->second, lowguid);
+ }
+ }
+
+ if (trans->GetSize()) // basically never false
+ CharacterDatabase.CommitTransaction(trans);
+ }
+
+ void OnLogin(Player* player, bool /*firstLogin*/) override
+ {
+ QueryResult result = CharacterDatabase.PQuery("SELECT GUID, FakeEntry FROM custom_transmogrification WHERE Owner = %u", player->GetGUID().GetCounter());
+
+ if (result)
+ {
+ do
+ {
+ Field* field = result->Fetch();
+ ObjectGuid itemGUID(HighGuid::Item, 0, field[0].GetUInt32());
+ uint32 fakeEntry = field[1].GetUInt32();
+ // Only load items that are in inventory / bank / etc
+ if (sObjectMgr->GetItemTemplate(fakeEntry) && player->GetItemByGuid(itemGUID))
+ {
+ player->transmogMap[itemGUID] = fakeEntry;
+ }
+ else
+ {
+ // Ignore, will be erased on next save.
+ // Additionally this can happen if an item was deleted from DB but still exists for the player
+ // TC_LOG_ERROR("custom.transmog", "Item entry (Entry: %u, itemGUID: %u, playerGUID: %u) does not exist, ignoring.", fakeEntry, GUID_LOPART(itemGUID), player->GetGUID().GetCounter());
+ // CharacterDatabase.PExecute("DELETE FROM custom_transmogrification WHERE FakeEntry = %u", fakeEntry);
+ }
+ } while (result->NextRow());
+
+ if (!player->transmogMap.empty())
+ {
+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
+ {
+ player->SetVisibleItemSlot(slot, item);
+ if (player->IsInWorld())
+ item->SendUpdateToPlayer(player);
+ }
+ }
+ }
+ }
+ }
+
+ void OnLogout(Player* player) override
+ {
+ TransmogDisplayVendorMgr::selectionStore.RemoveSelection(player->GetGUID().GetCounter());
+ }
+};
+#endif
+
+class PREP_TransmogDisplayVendor : public WorldScript
+{
+public:
+ PREP_TransmogDisplayVendor() : WorldScript("PREP_TransmogDisplayVendor") { }
+
+ void OnStartup() override
+ {
+ for (uint32 v : TransmogDisplayVendorMgr::AllowedItems)
+ TransmogDisplayVendorMgr::Allowed.push_back(v);
+ for (uint32 v : TransmogDisplayVendorMgr::NotAllowedItems)
+ TransmogDisplayVendorMgr::NotAllowed.push_back(v);
+
+ TC_LOG_INFO("server.loading", "Creating a list of usable transmogrification entries...");
+ // initialize .. for reload in future?
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
+ delete TransmogDisplayVendorMgr::optionMap[i][j][k], TransmogDisplayVendorMgr::optionMap[i][j][k] = nullptr;
+
+ std::unordered_set<uint32> displays;
+ for (auto const& v : sObjectMgr->GetItemTemplateStore())
+ {
+ auto const& temp = v.second;
+ if (temp.Class != ITEM_CLASS_WEAPON && temp.Class != ITEM_CLASS_ARMOR)
+ continue;
+ if (!TransmogDisplayVendorMgr::SuitableForTransmogrification(nullptr, &temp))
+ continue;
+ if (displays.find(temp.DisplayInfoID) != displays.end()) // skip duplicate item displays
+ continue;
+ TransmogDisplayVendorMgr::EntryVector* oM = TransmogDisplayVendorMgr::optionMap[(temp.Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + temp.SubClass][TransmogDisplayVendorMgr::getCorrectInvType(temp.InventoryType)][temp.Quality];
+ if (!oM)
+ {
+ oM = new TransmogDisplayVendorMgr::EntryVector();
+ TransmogDisplayVendorMgr::optionMap[(temp.Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + temp.SubClass][TransmogDisplayVendorMgr::getCorrectInvType(temp.InventoryType)][temp.Quality] = oM;
+ }
+ if (oM->size() < MAX_VENDOR_ITEMS * 3)
+ {
+ oM->push_back(temp.ItemId);
+ displays.insert(temp.DisplayInfoID);
+ }
+ else
+ {
+ TC_LOG_INFO("server.loading", "Too many items for transmogrification: Class: %u SubClass: %u InventoryType: %u Quality: %u", temp.Class, temp.SubClass, TransmogDisplayVendorMgr::getCorrectInvType(temp.InventoryType), temp.Quality);
+ }
+ }
+
+ // resize entry lists
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
+ if (TransmogDisplayVendorMgr::optionMap[i][j][k])
+ TransmogDisplayVendorMgr::optionMap[i][j][k]->resize(TransmogDisplayVendorMgr::optionMap[i][j][k]->size());
+
+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
+ TC_LOG_INFO("custom.transmog", "Deleting non-existing transmogrification entries...");
+ CharacterDatabase.DirectExecute("DELETE FROM custom_transmogrification WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_transmogrification.GUID)");
+#endif
+ }
+
+ void OnShutdown() override
+ {
+ for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON + MAX_ITEM_SUBCLASS_ARMOR; ++i)
+ for (uint32 j = 0; j < MAX_INVTYPE; ++j)
+ for (uint32 k = 0; k < MAX_ITEM_QUALITY; ++k)
+ delete TransmogDisplayVendorMgr::optionMap[i][j][k], TransmogDisplayVendorMgr::optionMap[i][j][k] = nullptr;
+ }
+};
+
+void AddSC_NPC_TransmogDisplayVendor()
+{
+ new NPC_TransmogDisplayVendor();
+ new PREP_TransmogDisplayVendor();
+
+#if !TRANSMOGRIFICATION_ALREADY_INSTALLED
+ new Player_Transmogrify();
+#endif
+}
diff --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
new file mode 100644
index 000000000000..0e675b585b45
--- /dev/null
+++ b/src/server/scripts/Custom/TransmogDisplayVendor/sql/updates/characters_update_6_1_to_6_2.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `custom_transmogrification`
+ COMMENT='6_2',
+ COLLATE='utf8_general_ci';
diff --git a/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql b/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql
new file mode 100644
index 000000000000..62bfdd227243
--- /dev/null
+++ b/src/server/scripts/Custom/TransmogDisplayVendor/sql/world_NPC.sql
@@ -0,0 +1,6 @@
+SET
+@Entry = 190011,
+@Name = "Narpweaver";
+
+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`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES
+(@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, 1, 0, 0, 1, 0, 0, 'NPC_TransmogDisplayVendor');
diff --git a/src/server/scripts/Custom/custom_script_loader.cpp b/src/server/scripts/Custom/custom_script_loader.cpp
index 9e5e9ba2bfd6..ff98419fa3c9 100644
--- a/src/server/scripts/Custom/custom_script_loader.cpp
+++ b/src/server/scripts/Custom/custom_script_loader.cpp
@@ -16,9 +16,11 @@
*/
// This is where scripts' loading functions should be declared:
+void AddSC_NPC_TransmogDisplayVendor();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
void AddCustomScripts()
{
+ AddSC_NPC_TransmogDisplayVendor();
}
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 20723bfdff3d..c73dbcd65427 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -22,6 +22,7 @@
* Scriptnames of files in this file should be prefixed with "spell_gen_"
*/
+#include "TransmogDisplayVendorConf.h"
#include "ScriptMgr.h"
#include "Battleground.h"
#include "CellImpl.h"
@@ -970,7 +971,12 @@ class spell_gen_clone_weapon_aura : public AuraScript
if (Player* player = caster->ToPlayer())
{
if (Item* mainItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry());
+ {
+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(mainItem))
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, entry);
+ else
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry());
+ }
}
else
target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID));
@@ -984,7 +990,12 @@ class spell_gen_clone_weapon_aura : public AuraScript
if (Player* player = caster->ToPlayer())
{
if (Item* offItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry());
+ {
+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(offItem))
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, entry);
+ else
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry());
+ }
}
else
target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1));
@@ -997,7 +1008,12 @@ class spell_gen_clone_weapon_aura : public AuraScript
if (Player* player = caster->ToPlayer())
{
if (Item* rangedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
- target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry());
+ {
+ if (uint32 entry = TransmogDisplayVendorMgr::GetFakeEntry(rangedItem))
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, entry);
+ else
+ target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry());
+ }
}
else
target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2));