mirror of
https://github.com/ornfelt/azerothcore_lua_scripts
synced 2026-08-22 06:26:03 -04:00
1630 lines
74 KiB
Diff
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 [](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));
|