Advertisement
Rochet2

Untitled

Jul 21st, 2015
518
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 38.95 KB | None | 0 0
  1. /*
  2. Transmog display vendor
  3. Code by Rochet2
  4. Ideas LilleCarl
  5.  
  6. ScriptName for NPC:
  7. NPC_TransmogDisplayVendor
  8.  
  9. Compatible with Transmogrification 6.1 by Rochet2
  10. https://rochet2.github.io/?page=Transmogrification
  11. */
  12.  
  13. #include "ScriptPCH.h"
  14. #include "TransmogDisplayVendorConf.h"
  15.  
  16. // Config start
  17.  
  18. // Edit Transmogrification compatibility in TransmogDisplayVendorConf.h
  19.  
  20. // A multiplier for the default gold cost (change to 0.0f for no default cost)
  21. const float TransmogDisplayVendorMgr::ScaledCostModifier = 0.0f;
  22. // Cost added on top of other costs (can be negative)
  23. const int32 TransmogDisplayVendorMgr::CopperCost = 0;
  24. // For custom gold cost set ScaledCostModifier to 0.0f and CopperCost to what ever cost you want
  25.  
  26. const bool TransmogDisplayVendorMgr::RequireToken = false;
  27. const uint32 TransmogDisplayVendorMgr::TokenEntry = 49426;
  28. const uint32 TransmogDisplayVendorMgr::TokenAmount = 1;
  29.  
  30. const bool TransmogDisplayVendorMgr::AllowPoor = true;
  31. const bool TransmogDisplayVendorMgr::AllowCommon = true;
  32. const bool TransmogDisplayVendorMgr::AllowUncommon = true;
  33. const bool TransmogDisplayVendorMgr::AllowRare = true;
  34. const bool TransmogDisplayVendorMgr::AllowEpic = true;
  35. const bool TransmogDisplayVendorMgr::AllowLegendary = true;
  36. const bool TransmogDisplayVendorMgr::AllowArtifact = true;
  37. const bool TransmogDisplayVendorMgr::AllowHeirloom = true;
  38.  
  39. const bool TransmogDisplayVendorMgr::AllowMixedArmorTypes = false;
  40. const bool TransmogDisplayVendorMgr::AllowMixedWeaponTypes = false;
  41. const bool TransmogDisplayVendorMgr::AllowFishingPoles = false;
  42.  
  43. const bool TransmogDisplayVendorMgr::IgnoreReqRace = true;
  44. const bool TransmogDisplayVendorMgr::IgnoreReqClass = false;
  45. const bool TransmogDisplayVendorMgr::IgnoreReqSkill = true;
  46. const bool TransmogDisplayVendorMgr::IgnoreReqSpell = true;
  47. const bool TransmogDisplayVendorMgr::IgnoreReqLevel = true;
  48. const bool TransmogDisplayVendorMgr::IgnoreReqEvent = true;
  49. const bool TransmogDisplayVendorMgr::IgnoreReqStats = true;
  50.  
  51. // Example AllowedItems[] = { 123, 234, 345 };
  52. static const uint32 AllowedItems[] = {0};
  53. static const uint32 NotAllowedItems[] = {0};
  54.  
  55. // Config end
  56.  
  57. std::set<uint32> TransmogDisplayVendorMgr::Allowed;
  58. std::set<uint32> TransmogDisplayVendorMgr::NotAllowed;
  59.  
  60. #ifdef BOOST_VERSION
  61. #define USING_BOOST
  62. #endif
  63. #ifdef USING_BOOST
  64. #include <boost/thread/locks.hpp>
  65. #include <boost/thread/shared_mutex.hpp>
  66. #endif
  67.  
  68. namespace
  69. {
  70.     class SelectionStore
  71.     {
  72.     public:
  73.         typedef std::mutex LockType;
  74.         typedef std::unique_lock<LockType> WriteGuard;
  75.  
  76.         void SetSlot(uint64 plrGuid, uint8 slot)
  77.         {
  78.             WriteGuard guard(lock);
  79.             hashmap[plrGuid] = slot;
  80.         }
  81.  
  82.         bool GetSlot(uint64 plrGuid, uint8& slot)
  83.         {
  84.             WriteGuard guard(lock);
  85.  
  86.             auto it = hashmap.find(plrGuid);
  87.             if (it == hashmap.end())
  88.                 return false;
  89.  
  90.             slot = it->second;
  91.             return true;
  92.         }
  93.  
  94.         void RemoveData(uint64 plrGuid)
  95.         {
  96.             WriteGuard guard(lock);
  97.             hashmap.erase(plrGuid);
  98.         }
  99.  
  100.     private:
  101.         std::mutex lock;
  102.         std::unordered_map<uint64, uint8> hashmap; // guid to slot
  103.     };
  104. };
  105.  
  106. // Selection store
  107. static SelectionStore selectionStore; // selectionStore[GUID] = Slot
  108.  
  109. // Vendor data store
  110. struct ItemData
  111. {
  112.     uint32 entry;
  113.     uint32 rating;
  114. };
  115. static const std::vector<ItemData> itemList;
  116.  
  117. uint32 TransmogDisplayVendorMgr::GetFakeEntry(const Item* item)
  118. {
  119.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetFakeEntry");
  120.  
  121.     Player* owner = item->GetOwner();
  122.  
  123.     if (!owner)
  124.         return 0;
  125.     if (owner->transmogMap.empty())
  126.         return 0;
  127.  
  128.     TransmogMapType::const_iterator it = owner->transmogMap.find(item->GetGUID());
  129.     if (it == owner->transmogMap.end())
  130.         return 0;
  131.     return it->second;
  132. }
  133. void TransmogDisplayVendorMgr::DeleteFakeEntry(Player* player, Item* item)
  134. {
  135.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::DeleteFakeEntry");
  136.  
  137.     if (player->transmogMap.erase(item->GetGUID()) != 0)
  138.         UpdateItem(player, item);
  139. }
  140. void TransmogDisplayVendorMgr::SetFakeEntry(Player* player, Item* item, uint32 entry)
  141. {
  142.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SetFakeEntry");
  143.  
  144.     player->transmogMap[item->GetGUID()] = entry;
  145.     UpdateItem(player, item);
  146. }
  147. void TransmogDisplayVendorMgr::UpdateItem(Player* player, Item* item)
  148. {
  149.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::UpdateItem");
  150.  
  151.     if (item->IsEquipped())
  152.     {
  153.         player->SetVisibleItemSlot(item->GetSlot(), item);
  154.         if (player->IsInWorld())
  155.             item->SendUpdateToPlayer(player);
  156.     }
  157. }
  158. const char* TransmogDisplayVendorMgr::getSlotName(uint8 slot, WorldSession* /*session*/)
  159. {
  160.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::TransmogDisplayVendorMgr::getSlotName");
  161.  
  162.     switch (slot)
  163.     {
  164.         case EQUIPMENT_SLOT_HEAD: return  "Head";// session->GetTrinityString(LANG_SLOT_NAME_HEAD);
  165.         case EQUIPMENT_SLOT_SHOULDERS: return  "Shoulders";// session->GetTrinityString(LANG_SLOT_NAME_SHOULDERS);
  166.         case EQUIPMENT_SLOT_BODY: return  "Shirt";// session->GetTrinityString(LANG_SLOT_NAME_BODY);
  167.         case EQUIPMENT_SLOT_CHEST: return  "Chest";// session->GetTrinityString(LANG_SLOT_NAME_CHEST);
  168.         case EQUIPMENT_SLOT_WAIST: return  "Waist";// session->GetTrinityString(LANG_SLOT_NAME_WAIST);
  169.         case EQUIPMENT_SLOT_LEGS: return  "Legs";// session->GetTrinityString(LANG_SLOT_NAME_LEGS);
  170.         case EQUIPMENT_SLOT_FEET: return  "Feet";// session->GetTrinityString(LANG_SLOT_NAME_FEET);
  171.         case EQUIPMENT_SLOT_WRISTS: return  "Wrists";// session->GetTrinityString(LANG_SLOT_NAME_WRISTS);
  172.         case EQUIPMENT_SLOT_HANDS: return  "Hands";// session->GetTrinityString(LANG_SLOT_NAME_HANDS);
  173.         case EQUIPMENT_SLOT_BACK: return  "Back";// session->GetTrinityString(LANG_SLOT_NAME_BACK);
  174.         case EQUIPMENT_SLOT_MAINHAND: return  "Main hand";// session->GetTrinityString(LANG_SLOT_NAME_MAINHAND);
  175.         case EQUIPMENT_SLOT_OFFHAND: return  "Off hand";// session->GetTrinityString(LANG_SLOT_NAME_OFFHAND);
  176.         case EQUIPMENT_SLOT_RANGED: return  "Ranged";// session->GetTrinityString(LANG_SLOT_NAME_RANGED);
  177.         case EQUIPMENT_SLOT_TABARD: return  "Tabard";// session->GetTrinityString(LANG_SLOT_NAME_TABARD);
  178.         default: return nullptr;
  179.     }
  180. }
  181. uint32 TransmogDisplayVendorMgr::GetSpecialPrice(ItemTemplate const* proto)
  182. {
  183.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::GetSpecialPrice");
  184.  
  185.     uint32 cost = proto->SellPrice < 10000 ? 10000 : proto->SellPrice;
  186.     return cost;
  187. }
  188. bool TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source)
  189. {
  190.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem");
  191.  
  192.     if (!target || !source)
  193.         return false;
  194.  
  195.     if (source->ItemId == target->ItemId)
  196.         return false;
  197.  
  198.     if (!SuitableForTransmogrification(player, target) || !SuitableForTransmogrification(player, source)) // if (!transmogrified->CanTransmogrify() || !transmogrifier->CanBeTransmogrified())
  199.         return false;
  200.  
  201.     if (source->InventoryType == INVTYPE_BAG ||
  202.         source->InventoryType == INVTYPE_RELIC ||
  203.         // source->InventoryType == INVTYPE_BODY ||
  204.         source->InventoryType == INVTYPE_FINGER ||
  205.         source->InventoryType == INVTYPE_TRINKET ||
  206.         source->InventoryType == INVTYPE_AMMO ||
  207.         source->InventoryType == INVTYPE_QUIVER)
  208.         return false;
  209.  
  210.     // TC doesnt check this? Checked by Inventory type check.
  211.     if (source->Class != target->Class)
  212.         return false;
  213.  
  214.     if (source->SubClass != target->SubClass && !IsRangedWeapon(target->Class, target->SubClass))
  215.     {
  216.         if (source->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
  217.             return false;
  218.         if (source->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
  219.             return false;
  220.     }
  221.  
  222.     if (source->InventoryType != target->InventoryType)
  223.     {
  224.         if (source->Class == ITEM_CLASS_WEAPON &&
  225.             (IsRangedWeapon(target->Class, target->SubClass) != IsRangedWeapon(source->Class, source->SubClass) ||
  226.             source->InventoryType == INVTYPE_WEAPONMAINHAND ||
  227.             source->InventoryType == INVTYPE_WEAPONOFFHAND))
  228.             return false;
  229.         if (source->Class == ITEM_CLASS_ARMOR &&
  230.             !((source->InventoryType == INVTYPE_CHEST && target->InventoryType == INVTYPE_ROBE) ||
  231.             (source->InventoryType == INVTYPE_ROBE && target->InventoryType == INVTYPE_CHEST)))
  232.             return false;
  233.     }
  234.  
  235.     if (!IgnoreReqClass && (source->AllowableClass & player->getClassMask()) == 0)
  236.         return false;
  237.  
  238.     return true;
  239. }
  240. bool TransmogDisplayVendorMgr::SuitableForTransmogrification(Player* player, ItemTemplate const* proto)
  241. {
  242.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::SuitableForTransmogrification");
  243.  
  244.     // ItemTemplate const* proto = item->GetTemplate();
  245.     if (!proto)
  246.         return false;
  247.  
  248.     if (proto->Class != ITEM_CLASS_ARMOR &&
  249.         proto->Class != ITEM_CLASS_WEAPON)
  250.         return false;
  251.  
  252.     // Skip all checks for allowed items
  253.     if (IsAllowed(proto->ItemId))
  254.         return true;
  255.  
  256.     if (IsNotAllowed(proto->ItemId))
  257.         return false;
  258.  
  259.     if (!AllowFishingPoles && proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
  260.         return false;
  261.  
  262.     if (!IsAllowedQuality(proto->Quality)) // (proto->Quality == ITEM_QUALITY_LEGENDARY)
  263.         return false;
  264.  
  265.     if (player)
  266.     {
  267.         //if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && player->GetTeam() != HORDE)
  268.         //return false;
  269.  
  270.         //if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && player->GetTeam() != ALLIANCE)
  271.         //return false;
  272.  
  273.         //if (!IgnoreReqClass && (proto->AllowableClass & player->getClassMask()) == 0)
  274.         //return false;
  275.  
  276.         //if (!IgnoreReqRace && (proto->AllowableRace & player->getRaceMask()) == 0)
  277.         //return false;
  278.  
  279.         if (!IgnoreReqSkill && proto->RequiredSkill != 0)
  280.         {
  281.             if (player->GetSkillValue(proto->RequiredSkill) == 0)
  282.                 return false;
  283.             else if (player->GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
  284.                 return false;
  285.         }
  286.  
  287.         if (!IgnoreReqSpell && proto->RequiredSpell != 0 && !player->HasSpell(proto->RequiredSpell))
  288.             return false;
  289.  
  290.         if (!IgnoreReqLevel && player->getLevel() < proto->RequiredLevel)
  291.             return false;
  292.     }
  293.  
  294.     // If World Event is not active, prevent using event dependant items
  295.     if (!IgnoreReqEvent && proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
  296.         return false;
  297.  
  298.     if (!IgnoreReqStats)
  299.     {
  300.         if (!proto->RandomProperty && !proto->RandomSuffix)
  301.         {
  302.             bool found = false;
  303.             for (uint8 i = 0; i < proto->StatsCount; ++i)
  304.             {
  305.                 if (proto->ItemStat[i].ItemStatValue != 0)
  306.                 {
  307.                     found = true;
  308.                     break;
  309.                 }
  310.             }
  311.             if (!found)
  312.                 return false;
  313.         }
  314.     }
  315.  
  316.     return true;
  317. }
  318.  
  319. bool TransmogDisplayVendorMgr::IsRangedWeapon(uint32 Class, uint32 SubClass)
  320. {
  321.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsRangedWeapon");
  322.  
  323.     return Class == ITEM_CLASS_WEAPON && (
  324.         SubClass == ITEM_SUBCLASS_WEAPON_BOW ||
  325.         SubClass == ITEM_SUBCLASS_WEAPON_GUN ||
  326.         SubClass == ITEM_SUBCLASS_WEAPON_CROSSBOW);
  327. }
  328. bool TransmogDisplayVendorMgr::IsAllowed(uint32 entry)
  329. {
  330.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowed");
  331.  
  332.     return Allowed.find(entry) != Allowed.end();
  333. }
  334. bool TransmogDisplayVendorMgr::IsNotAllowed(uint32 entry)
  335. {
  336.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsNotAllowed");
  337.  
  338.     return NotAllowed.find(entry) != NotAllowed.end();
  339. }
  340. bool TransmogDisplayVendorMgr::IsAllowedQuality(uint32 quality)
  341. {
  342.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::IsAllowedQuality");
  343.  
  344.     switch (quality)
  345.     {
  346.         case ITEM_QUALITY_POOR: return AllowPoor;
  347.         case ITEM_QUALITY_NORMAL: return AllowCommon;
  348.         case ITEM_QUALITY_UNCOMMON: return AllowUncommon;
  349.         case ITEM_QUALITY_RARE: return AllowRare;
  350.         case ITEM_QUALITY_EPIC: return AllowEpic;
  351.         case ITEM_QUALITY_LEGENDARY: return AllowLegendary;
  352.         case ITEM_QUALITY_ARTIFACT: return AllowArtifact;
  353.         case ITEM_QUALITY_HEIRLOOM: return AllowHeirloom;
  354.         default: return false;
  355.     }
  356. }
  357.  
  358. static const char* getQualityName(uint32 quality)
  359. {
  360.     switch (quality)
  361.     {
  362.         case ITEM_QUALITY_POOR: return "|CFF9d9d9d[Poor]";
  363.         case ITEM_QUALITY_NORMAL: return "|CFFffffff[Common]";
  364.         case ITEM_QUALITY_UNCOMMON: return "|CFF1eff00[Uncommon]";
  365.         case ITEM_QUALITY_RARE: return "|CFF0070dd[Rare]";
  366.         case ITEM_QUALITY_EPIC: return "|CFFa335ee[Epic]";
  367.         case ITEM_QUALITY_LEGENDARY: return "|CFFff8000[Legendary]";
  368.         case ITEM_QUALITY_ARTIFACT: return "|CFFe6cc80[Artifact]";
  369.         case ITEM_QUALITY_HEIRLOOM: return "|CFFe5cc80[Heirloom]";
  370.         default: return "[Unknown]";
  371.     }
  372. }
  373.  
  374. static std::string getItemName(const ItemTemplate* itemTemplate, WorldSession* session)
  375. {
  376.     std::string name = itemTemplate->Name1;
  377.     int loc_idx = session->GetSessionDbLocaleIndex();
  378.     if (loc_idx >= 0)
  379.         if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
  380.             sObjectMgr->GetLocaleString(il->Name, loc_idx, name);
  381.     return name;
  382. }
  383.  
  384. static uint32 getCorrectInvType(uint32 inventorytype)
  385. {
  386.     switch (inventorytype)
  387.     {
  388.         case INVTYPE_WEAPONMAINHAND:
  389.         case INVTYPE_WEAPONOFFHAND:
  390.             return INVTYPE_WEAPON;
  391.         case INVTYPE_RANGEDRIGHT:
  392.             return INVTYPE_RANGED;
  393.         case INVTYPE_ROBE:
  394.             return INVTYPE_CHEST;
  395.         default:
  396.             return inventorytype;
  397.     }
  398. }
  399.  
  400. void TransmogDisplayVendorMgr::HandleTransmogrify(Player* player, Creature* /*creature*/, uint32 vendorslot, uint32 itemEntry, bool no_cost)
  401. {
  402.     TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify");
  403.  
  404.     uint8 slot;
  405.     if (!selectionStore.GetSlot(player->GetGUID(), slot))
  406.         return; // cheat, no slot selected
  407.  
  408.     const char* slotname = TransmogDisplayVendorMgr::getSlotName(slot, player->GetSession());
  409.     if (!slotname)
  410.         return;
  411.  
  412.     // slot of the transmogrified item
  413.     if (slot >= EQUIPMENT_SLOT_END)
  414.     {
  415.         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);
  416.         return; // LANG_ERR_TRANSMOG_INVALID_SLOT
  417.     }
  418.  
  419.     const ItemTemplate* itemTransmogrifier = nullptr;
  420.     // guid of the transmogrifier item, if it's not 0
  421.    if (itemEntry)
  422.    {
  423.        itemTransmogrifier = sObjectMgr->GetItemTemplate(itemEntry);
  424.        if (!itemTransmogrifier)
  425.        {
  426.            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);
  427.            return; // LANG_ERR_TRANSMOG_MISSING_SRC_ITEM
  428.        }
  429.    }
  430.  
  431.    // transmogrified item
  432.    Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  433.    if (!itemTransmogrified)
  434.    {
  435.        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);
  436.        player->GetSession()->SendNotification("No item in %s slot", slotname);
  437.        return; // LANG_ERR_TRANSMOG_MISSING_DEST_ITEM
  438.    }
  439.  
  440.    if (!itemTransmogrifier) // reset look newEntry
  441.    {
  442.        DeleteFakeEntry(player, itemTransmogrified);
  443.    }
  444.    else
  445.    {
  446.        if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier))
  447.        {
  448.            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);
  449.            player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
  450.            return; // LANG_ERR_TRANSMOG_INVALID_ITEMS
  451.        }
  452.  
  453.        if (uint32 fakeEntry = GetFakeEntry(itemTransmogrified))
  454.        {
  455.            if (const ItemTemplate* fakeItemTemplate = sObjectMgr->GetItemTemplate(fakeEntry))
  456.            {
  457.                if (fakeItemTemplate->DisplayInfoID == itemTransmogrifier->DisplayInfoID)
  458.                {
  459.                    return;
  460.                }
  461.            }
  462.        }
  463.  
  464.        const ItemData* item_data = nullptr;
  465.        for (auto&& data : itemList)
  466.        {
  467.            if (data.entry == itemEntry)
  468.                item_data = &data;
  469.        }
  470.  
  471.        if (!item_data)
  472.        {
  473.            player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
  474.            return; // either cheat or changed items (not found in correct place in transmog vendor view)
  475.        }
  476.  
  477.        auto Q = CharacterDatabase.PQuery("SELECT counter FROM character_achievement_progress WHERE criteria=451 AND guid=%u", player->GetGUIDLow());
  478.        uint32 twohighest = 0;
  479.  
  480.        if (Q)
  481.        {
  482.            Field* qfield = Q->Fetch();
  483.            twohighest = qfield[0].GetUInt32();
  484.        }
  485.  
  486.        if (twohighest < item_data->rating)
  487.        {
  488.            ChatHandler(player->GetSession()).PSendSysMessage("You need to have achieved %u 2v2 rating", item_data->rating);
  489.            return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_RATING
  490.        }
  491.  
  492.        if (!no_cost)
  493.        {
  494.            if (RequireToken)
  495.            {
  496.                if (player->HasItemCount(TokenEntry, TokenAmount))
  497.                {
  498.                    player->DestroyItemCount(TokenEntry, TokenAmount, true);
  499.                }
  500.                else
  501.                {
  502.                    player->GetSession()->SendNotification("You do not have enough %ss", getItemName(sObjectMgr->GetItemTemplate(TransmogDisplayVendorMgr::TokenEntry), player->GetSession()).c_str());
  503.                    return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS
  504.                }
  505.            }
  506.  
  507.            int32 cost = 0;
  508.            cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
  509.            cost *= ScaledCostModifier;
  510.            cost += CopperCost;
  511.  
  512.            if (cost) // 0 cost if reverting look
  513.            {
  514.                if (cost < 0)
  515.                {
  516.                    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);
  517.                }
  518.                else
  519.                {
  520.                    if (!player->HasEnoughMoney(cost))
  521.                    {
  522.                        player->GetSession()->SendNotification("You do not have enough money");
  523.                        return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY
  524.                    }
  525.                    player->ModifyMoney(-cost, false);
  526.                }
  527.            }
  528.  
  529.            SetFakeEntry(player, itemTransmogrified, itemTransmogrifier->ItemId);
  530.  
  531.            itemTransmogrified->UpdatePlayedTime(player);
  532.  
  533.            itemTransmogrified->SetOwnerGUID(player->GetGUID());
  534.            itemTransmogrified->SetNotRefundable(player);
  535.            itemTransmogrified->ClearSoulboundTradeable(player);
  536.  
  537.            //if (itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_USE)
  538.            //    itemTransmogrifier->SetBinding(true);
  539.  
  540.            //itemTransmogrifier->SetOwnerGUID(player->GetGUID());
  541.            //itemTransmogrifier->SetNotRefundable(player);
  542.            //itemTransmogrifier->ClearSoulboundTradeable(player);
  543.        }
  544.  
  545.        player->PlayDirectSound(3337);
  546.        player->GetSession()->SendAreaTriggerMessage("%s transmogrified", slotname);
  547.        //return LANG_ERR_TRANSMOG_OK;
  548.    }
  549. }
  550.  
  551. class NPC_TransmogDisplayVendor : public CreatureScript
  552. {
  553. public:
  554.    NPC_TransmogDisplayVendor() : CreatureScript("NPC_TransmogDisplayVendor")
  555.    {
  556.    } // If you change this, also change in Player.cpp: if (creature->GetScriptName() == "NPC_TransmogDisplayVendor")
  557.  
  558.    bool OnGossipHello(Player* player, Creature* creature) override
  559.    {
  560.        player->PlayerTalkClass->ClearMenus();
  561.        WorldSession* session = player->GetSession();
  562.        for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
  563.        {
  564.            if (slot == 0 || slot == 1 || slot == 3 || slot == 10 || slot == 11 || slot == 12 || slot == 13
  565.                || slot == 18 || slot == 15 || slot == 16)
  566.                continue;
  567.  
  568.            // if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
  569.            if (const char* slotName = TransmogDisplayVendorMgr::getSlotName(slot, session))
  570.                player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, slotName, SENDER_SELECT_VENDOR, slot);
  571.        }
  572.        player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, "Remove transmogrifications", SENDER_REMOVE_MENU, 0);
  573.        player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
  574.        return true;
  575.    }
  576.  
  577.    bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
  578.    {
  579.        WorldSession* session = player->GetSession();
  580.        player->PlayerTalkClass->ClearMenus();
  581.        switch (sender)
  582.        {
  583.            case SENDER_SELECT_VENDOR: // action = slot
  584.            {
  585.                Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action);
  586.                if (!item)
  587.                {
  588.                    if (const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession()))
  589.                        session->SendNotification("No item equipped in %s slot", slotname);
  590.                    OnGossipHello(player, creature);
  591.                    return true;
  592.                }
  593.  
  594.                // items to show in vendor
  595.                std::vector< std::pair<const ItemTemplate*, uint32> > vendorItems;
  596.  
  597.                ItemTemplate const* itemTemplate = item->GetTemplate();
  598.                for (auto&& data : itemList)
  599.                {
  600.                    ItemTemplate const* curtemp = sObjectMgr->GetItemTemplate(data.entry);
  601.                    if (!curtemp)
  602.                        continue;
  603.  
  604.                    if (!TransmogDisplayVendorMgr::CanTransmogrifyItemWithItem(player, itemTemplate, curtemp))
  605.                        continue;
  606.  
  607.                    vendorItems.push_back(std::make_pair(curtemp, data.rating));
  608.                }
  609.  
  610.                player->CLOSE_GOSSIP_MENU();
  611.  
  612.                TC_LOG_DEBUG("network", "WORLD: Sent SMSG_LIST_INVENTORY");
  613.  
  614.                Creature* vendor = player->GetNPCIfCanInteractWith(creature->GetGUID(), UNIT_NPC_FLAG_VENDOR);
  615.                if (!vendor)
  616.                {
  617.                    TC_LOG_DEBUG("network", "WORLD: SendListInventory - Unit (GUID: %u) not found or you can not interact with him.", creature->GetGUIDLow());
  618.                    player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty, 0);
  619.                    return true;
  620.                }
  621.  
  622.                if (player->HasUnitState(UNIT_STATE_DIED))
  623.                    player->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
  624.  
  625.                if (vendor->HasUnitState(UNIT_STATE_MOVING))
  626.                    vendor->StopMoving();
  627.  
  628.                uint8 count = 0;
  629.  
  630.                WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + vendorItems.size() * 8 * 4);
  631.                data << uint64(creature->GetGUID());
  632.  
  633.                size_t countPos = data.wpos();
  634.                data << uint8(count);
  635.  
  636.                uint32 item_amount = 0;
  637.                for (auto&& vendorItem : vendorItems)
  638.                {
  639.                    if (item_amount >= MAX_VENDOR_ITEMS)
  640.                    {
  641.                        TC_LOG_ERROR("custom.transmog", "transmog_vendor_items has too many items for slot %u, showing only %u", static_cast<uint32>(action), static_cast<uint32>(MAX_VENDOR_ITEMS));
  642.                        break;
  643.                    }
  644.  
  645.                    auto Q = CharacterDatabase.PQuery("SELECT counter FROM character_achievement_progress WHERE criteria=451 AND guid=%u", player->GetGUIDLow());
  646.                    uint32 twohighest = 0;
  647.  
  648.                    if (Q)
  649.                    {
  650.                        Field* qfield = Q->Fetch();
  651.                        twohighest = qfield[0].GetUInt32();
  652.                    }
  653.  
  654.                    bool grey = false;
  655.                    if (twohighest < vendorItem.second)
  656.                        grey = true;
  657.  
  658.                    data << uint32(count + 1);
  659.                    data << uint32(vendorItem.first->ItemId);
  660.                    data << uint32(vendorItem.first->DisplayInfoID);
  661.                    if (!grey)
  662.                        data << int32(0xFFFFFFFF);
  663.                    else
  664.                        data << int32(0);
  665.                    data << uint32(0);
  666.                    data << uint32(vendorItem.first->MaxDurability);
  667.                    data << uint32(vendorItem.first->BuyCount);
  668.                    data << uint32(0);
  669.                    ++item_amount;
  670.                }
  671.  
  672.                if (!item_amount)
  673.                {
  674.                    session->SendAreaTriggerMessage("No transmogrifications found for equipped item");
  675.                    OnGossipHello(player, creature);
  676.                    return true;
  677.                }
  678.                else
  679.                {
  680.                    data.put<uint8>(countPos, item_amount);
  681.                    selectionStore.SetSlot(player->GetGUID(), action);
  682.                    session->SendPacket(&data);
  683.                }
  684.            } break;
  685.            case SENDER_BACK: // Back
  686.            {
  687.                OnGossipHello(player, creature);
  688.            } break;
  689.            case SENDER_REMOVE_ALL: // Remove TransmogDisplayVendorMgrs
  690.            {
  691.                bool removed = false;
  692.                for (uint8 Slot = EQUIPMENT_SLOT_START; Slot < EQUIPMENT_SLOT_END; Slot++)
  693.                {
  694.                    if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, Slot))
  695.                    {
  696.                        if (!TransmogDisplayVendorMgr::GetFakeEntry(newItem))
  697.                            continue;
  698.                        TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
  699.                        removed = true;
  700.                    }
  701.                }
  702.                if (removed)
  703.                {
  704.                    session->SendAreaTriggerMessage("Transmogrifications removed from equipped items");
  705.                    player->PlayDirectSound(3337);
  706.                }
  707.                else
  708.                {
  709.                    session->SendNotification("You have no transmogrified items equipped");
  710.                }
  711.                OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
  712.            } break;
  713.            case SENDER_REMOVE_ONE: // Remove TransmogDisplayVendorMgr from single item
  714.            {
  715.                const char* slotname = TransmogDisplayVendorMgr::getSlotName(action, player->GetSession());
  716.                if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
  717.                {
  718.                    if (TransmogDisplayVendorMgr::GetFakeEntry(newItem))
  719.                    {
  720.                        TransmogDisplayVendorMgr::DeleteFakeEntry(player, newItem);
  721.                        if (slotname)
  722.                            session->SendAreaTriggerMessage("%s transmogrification removed", slotname);
  723.                        player->PlayDirectSound(3337);
  724.                    }
  725.                    else if (slotname)
  726.                    {
  727.                        session->SendNotification("No transmogrification on %s slot", slotname);
  728.                    }
  729.                }
  730.                else if (slotname)
  731.                {
  732.                    session->SendNotification("No item equipped in %s slot", slotname);
  733.                }
  734.                OnGossipSelect(player, creature, SENDER_REMOVE_MENU, 0);
  735.            } break;
  736.            case SENDER_REMOVE_MENU:
  737.            {
  738.                for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
  739.                {
  740.                    const char* slotname = TransmogDisplayVendorMgr::getSlotName(slot, player->GetSession());
  741.                    if (!slotname)
  742.                        continue;
  743.                    std::ostringstream ss;
  744.                    ss << "Remove transmogrification from " << slotname << "?";
  745.                    player->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_INTERACT_1, (std::string)"Remove from " + slotname, SENDER_REMOVE_ONE, slot, ss.str().c_str(), 0, false);
  746.                }
  747.                player->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_INTERACT_1, "Remove all transmogrifications", SENDER_REMOVE_ALL, 0, "Are you sure you want to remove all transmogrifications?", 0, false);
  748.                player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TALK, "Back..", SENDER_BACK, 0);
  749.                player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
  750.            } break;
  751.        }
  752.        return true;
  753.    }
  754. };
  755. class Player_Transmogrify : public PlayerScript
  756. {
  757. public:
  758.    Player_Transmogrify() : PlayerScript("Player_Transmogrify")
  759.    {
  760.    }
  761.  
  762.    std::vector<ObjectGuid> GetItemList(const Player* player) const
  763.    {
  764.        std::vector<ObjectGuid> itemlist;
  765.  
  766.        // Copy paste from Player::GetItemByGuid(guid)
  767.  
  768.        for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  769.            if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  770.                itemlist.push_back(pItem->GetGUID());
  771.  
  772.        for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
  773.            if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  774.                itemlist.push_back(pItem->GetGUID());
  775.  
  776.        for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
  777.            if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  778.                itemlist.push_back(pItem->GetGUID());
  779.  
  780.        for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  781.            if (Bag* pBag = player->GetBagByPos(i))
  782.                for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  783.                    if (Item* pItem = pBag->GetItemByPos(j))
  784.                        itemlist.push_back(pItem->GetGUID());
  785.  
  786.        for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  787.            if (Bag* pBag = player->GetBagByPos(i))
  788.                for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  789.                    if (Item* pItem = pBag->GetItemByPos(j))
  790.                        itemlist.push_back(pItem->GetGUID());
  791.  
  792.        return itemlist;
  793.    }
  794.  
  795.    void OnSave(Player* player) override
  796.    {
  797.        uint32 lowguid = player->GetGUIDLow();
  798.        SQLTransaction trans = CharacterDatabase.BeginTransaction();
  799.        trans->PAppend("DELETE FROM `custom_transmogrification` WHERE `Owner` = %u", lowguid);
  800.  
  801.        if (!player->transmogMap.empty())
  802.        {
  803.            // Only save items that are in inventory / bank / etc
  804.            std::vector<ObjectGuid> items = GetItemList(player);
  805.            for (std::vector<ObjectGuid>::const_iterator it = items.begin(); it != items.end(); ++it)
  806.            {
  807.                TransmogMapType::const_iterator it2 = player->transmogMap.find(*it);
  808.                if (it2 == player->transmogMap.end())
  809.                    continue;
  810.  
  811.                trans->PAppend("REPLACE INTO custom_transmogrification (GUID, FakeEntry, Owner) VALUES (%u, %u, %u)", it2->first.GetCounter(), it2->second, lowguid);
  812.            }
  813.        }
  814.  
  815.        if (trans->GetSize()) // basically never false
  816.            CharacterDatabase.CommitTransaction(trans);
  817.    }
  818.  
  819.    void OnLogin(Player* player, bool /*firstLogin*/) override
  820.    {
  821.        QueryResult result = CharacterDatabase.PQuery("SELECT GUID, FakeEntry FROM custom_transmogrification WHERE Owner = %u", player->GetGUIDLow());
  822.  
  823.        if (result)
  824.        {
  825.            do
  826.            {
  827.                Field* field = result->Fetch();
  828.                ObjectGuid itemGUID(HIGHGUID_ITEM, 0, field[0].GetUInt32());
  829.                uint32 fakeEntry = field[1].GetUInt32();
  830.                // Only load items that are in inventory / bank / etc
  831.                if (sObjectMgr->GetItemTemplate(fakeEntry) && player->GetItemByGuid(itemGUID))
  832.                {
  833.                    player->transmogMap[itemGUID] = fakeEntry;
  834.                }
  835.                else
  836.                {
  837.                    // Ignore, will be erased on next save.
  838.                    // Additionally this can happen if an item was deleted from DB but still exists for the player
  839.                    // TC_LOG_ERROR("custom.transmog", "Item entry (Entry: %u, itemGUID: %u, playerGUID: %u) does not exist, ignoring.", fakeEntry, GUID_LOPART(itemGUID), player->GetGUIDLow());
  840.                    // CharacterDatabase.PExecute("DELETE FROM custom_transmogrification WHERE FakeEntry = %u", fakeEntry);
  841.                }
  842.            } while (result->NextRow());
  843.  
  844.            if (!player->transmogMap.empty())
  845.            {
  846.                for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  847.                {
  848.                    if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
  849.                    {
  850.                        player->SetVisibleItemSlot(slot, item);
  851.                        if (player->IsInWorld())
  852.                            item->SendUpdateToPlayer(player);
  853.                    }
  854.                }
  855.            }
  856.        }
  857.    }
  858.  
  859.    void OnLogout(Player* player) override
  860.    {
  861.        selectionStore.RemoveData(player->GetGUID());
  862.    }
  863. };
  864.  
  865. class PREP_TransmogDisplayVendor : public WorldScript
  866. {
  867. public:
  868.    PREP_TransmogDisplayVendor() : WorldScript("PREP_TransmogDisplayVendor")
  869.    {
  870.    }
  871.  
  872.    void OnStartup() override
  873.    {
  874.        // perform a const cast on the item list so we can modify it in this function
  875.        // otherwise it should be read only data!
  876.        std::vector<ItemData>& mod_itemList = const_cast<std::vector<ItemData>&>(itemList);
  877.  
  878.        for (size_t i = 0; i < sizeof(AllowedItems) / sizeof(*AllowedItems); ++i)
  879.            TransmogDisplayVendorMgr::Allowed.insert(AllowedItems[i]);
  880.        for (size_t i = 0; i < sizeof(NotAllowedItems) / sizeof(*NotAllowedItems); ++i)
  881.            TransmogDisplayVendorMgr::NotAllowed.insert(NotAllowedItems[i]);
  882.  
  883.        TC_LOG_INFO("server.loading", "Creating a list of usable transmogrification entries...");
  884.        // clear for reload
  885.        mod_itemList.clear();
  886.  
  887.        if (auto Q = WorldDatabase.PQuery("SELECT entry, rating FROM transmog_vendor_items"))
  888.        {
  889.            do
  890.            {
  891.                ItemData data;
  892.                data.entry = Q->Fetch()[0].GetUInt32();
  893.                data.rating = Q->Fetch()[1].GetUInt32();
  894.                if (auto itrsecond = sObjectMgr->GetItemTemplate(data.entry))
  895.                    mod_itemList.push_back(data);
  896.                else
  897.                    TC_LOG_ERROR("custom.transmog", "transmog_vendor_items has a non-existing item entry %u", data.entry);
  898.            } while (Q->NextRow());
  899.        }
  900.  
  901.        // resize entry list
  902.        mod_itemList.shrink_to_fit();
  903.  
  904.        TC_LOG_INFO("custom.transmog", "Deleting non-existing transmogrification entries...");
  905.        CharacterDatabase.DirectExecute("DELETE FROM custom_transmogrification WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_transmogrification.GUID)");
  906.    }
  907. };
  908.  
  909. class transmogcommands : public CommandScript
  910. {
  911. public:
  912.    transmogcommands() : CommandScript("transmogcommands")
  913.    {
  914.    }
  915.  
  916.    ChatCommand* GetCommands() const
  917.    {
  918.        static ChatCommand transmogCommandTable[] =
  919.        {
  920.            {"reload", rbac::RBAC_PERM_COMMAND_RELOAD, true, &HandleReloadTransmog, "", NULL},
  921.            {NULL, 0, false, NULL, "", NULL}
  922.        };
  923.        static ChatCommand commandTable[] =
  924.        {
  925.            {"transmog", rbac::RBAC_PERM_COMMAND_RELOAD, false, NULL, "", transmogCommandTable},
  926.            {NULL, 0, false, NULL, "", NULL}
  927.        };
  928.        return commandTable;
  929.    }
  930.  
  931.    static bool HandleReloadTransmog(ChatHandler* handler, const char* args)
  932.    {
  933.  
  934.        // perform a const cast on the item list so we can modify it in this function
  935.        // otherwise it should be read only data!
  936.        std::vector<ItemData>& mod_itemList = const_cast<std::vector<ItemData>&>(itemList);
  937.  
  938.        for (size_t i = 0; i < sizeof(AllowedItems) / sizeof(*AllowedItems); ++i)
  939.            TransmogDisplayVendorMgr::Allowed.insert(AllowedItems[i]);
  940.        for (size_t i = 0; i < sizeof(NotAllowedItems) / sizeof(*NotAllowedItems); ++i)
  941.            TransmogDisplayVendorMgr::NotAllowed.insert(NotAllowedItems[i]);
  942.  
  943.        TC_LOG_INFO("server.loading", "Creating a list of usable transmogrification entries...");
  944.        // clear for reload
  945.        mod_itemList.clear();
  946.  
  947.        if (auto Q = WorldDatabase.PQuery("SELECT entry, rating FROM transmog_vendor_items"))
  948.        {
  949.            do
  950.            {
  951.                ItemData data;
  952.                data.entry = Q->Fetch()[0].GetUInt32();
  953.                data.rating = Q->Fetch()[1].GetUInt32();
  954.                if (auto itrsecond = sObjectMgr->GetItemTemplate(data.entry))
  955.                    mod_itemList.push_back(data);
  956.                else
  957.                    TC_LOG_ERROR("custom.transmog", "transmog_vendor_items has a non-existing item entry %u", data.entry);
  958.            } while (Q->NextRow());
  959.        }
  960.  
  961.        // resize entry list
  962.        mod_itemList.shrink_to_fit();
  963.  
  964.        TC_LOG_INFO("custom.transmog", "Deleting non-existing transmogrification entries...");
  965.        CharacterDatabase.DirectExecute("DELETE FROM custom_transmogrification WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_transmogrification.GUID)");
  966.        return true;
  967.    }
  968. };
  969.  
  970. void AddSC_NPC_TransmogDisplayVendor()
  971. {
  972.    new NPC_TransmogDisplayVendor();
  973.    new PREP_TransmogDisplayVendor();
  974.    new Player_Transmogrify();
  975.    new transmogcommands();
  976. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement