From 488521520341c5fd7719a2e6e9a5d2bb292cd170 Mon Sep 17 00:00:00 2001 From: ZhengPeiRu21 <98835050+ZhengPeiRu21@users.noreply.github.com> Date: Mon, 23 May 2022 02:23:00 -0600 Subject: [PATCH 1/2] feat: Implement Appearance Collection Cache (#80) * Implement Appearance Collection cache * Do not add item to Appearance collection if it has BoP Tradable flag * Do not add item to Appearance collection if it has Refundable flag * Remove redundant include --- src/Transmogrification.cpp | 16 ++++ src/Transmogrification.h | 3 + src/transmog_scripts.cpp | 153 +++++++++++++++++++++---------------- 3 files changed, 105 insertions(+), 67 deletions(-) diff --git a/src/Transmogrification.cpp b/src/Transmogrification.cpp index 60387b6..250a8b6 100644 --- a/src/Transmogrification.cpp +++ b/src/Transmogrification.cpp @@ -293,6 +293,22 @@ void Transmogrification::SetFakeEntry(Player* player, uint32 newEntry, uint8 /*s UpdateItem(player, itemTransmogrified); } +bool Transmogrification::AddCollectedAppearance(uint32 accountId, uint32 itemId) +{ + if (collectionCache.find(accountId) == collectionCache.end()) + { + collectionCache.insert({accountId, {itemId}}); + return true; + } + if (std::find(collectionCache[accountId].begin(), collectionCache[accountId].end(), itemId) == collectionCache[accountId].end()) + { + collectionCache[accountId].push_back(itemId); + std::sort(collectionCache[accountId].begin(), collectionCache[accountId].end()); + return true; + } + return false; +} + TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost) { Item* itemTransmogrifier = Item::CreateItem(itemEntry, 1, 0); return Transmogrify(player, itemTransmogrifier, slot, no_cost); diff --git a/src/Transmogrification.h b/src/Transmogrification.h index 8787961..c96a813 100644 --- a/src/Transmogrification.h +++ b/src/Transmogrification.h @@ -57,8 +57,10 @@ public: typedef std::unordered_map transmogData; typedef std::unordered_map transmog2Data; typedef std::unordered_map transmogMap; + typedef std::unordered_map> collectionCacheMap; transmogMap entryMap; // entryMap[pGUID][iGUID] = entry transmogData dataMap; // dataMap[iGUID] = pGUID + collectionCacheMap collectionCache; #ifdef PRESETS bool EnableSetInfo; @@ -145,6 +147,7 @@ public: void UpdateItem(Player* player, Item* item) const; void DeleteFakeEntry(Player* player, uint8 slot, Item* itemTransmogrified, CharacterDatabaseTransaction* trans = nullptr); void SetFakeEntry(Player* player, uint32 newEntry, uint8 slot, Item* itemTransmogrified); + bool AddCollectedAppearance(uint32 accountId, uint32 itemId); TransmogAcoreStrings Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost = false); TransmogAcoreStrings Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost = false); diff --git a/src/transmog_scripts.cpp b/src/transmog_scripts.cpp index d87242f..a80a9be 100644 --- a/src/transmog_scripts.cpp +++ b/src/transmog_scripts.cpp @@ -22,6 +22,7 @@ Cant transmogrify rediculus items // Foereaper: would be fun to stab people with #include "Transmogrification.h" #include "ScriptedCreature.h" +#include "ItemTemplate.h" #define sT sTransmogrification #define GTS session->GetAcoreString // dropped translation support, no one using? @@ -358,70 +359,66 @@ public: { sendGossip = false; - std::string query = "SELECT item_template_id FROM custom_unlocked_appearances WHERE account_id = " + std::to_string(player->GetSession()->GetAccountId()) + " ORDER BY item_template_id"; - session->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(query).WithCallback([=](QueryResult result) + uint16 pageNumber = 0; + uint32 startValue = 0; + uint32 endValue = MAX_OPTIONS - 3; + bool lastPage = false; + if (gossipPageNumber > EQUIPMENT_SLOT_END + 10) { - uint16 pageNumber = 0; - uint32 startValue = 0; - uint32 endValue = MAX_OPTIONS - 3; - bool lastPage = false; - if (gossipPageNumber > EQUIPMENT_SLOT_END + 10) - { - pageNumber = gossipPageNumber - EQUIPMENT_SLOT_END - 10; - startValue = (pageNumber * (MAX_OPTIONS - 2)); - endValue = (pageNumber + 1) * (MAX_OPTIONS - 2) - 1; + pageNumber = gossipPageNumber - EQUIPMENT_SLOT_END - 10; + startValue = (pageNumber * (MAX_OPTIONS - 2)); + endValue = (pageNumber + 1) * (MAX_OPTIONS - 2) - 1; + } + uint32 accountId = player->GetSession()->GetAccountId(); + if (sT->collectionCache.find(accountId) != sT->collectionCache.end()) + { + std::vector allowedItems; + for (uint32 newItemEntryId : sT->collectionCache[accountId]) { + Item* newItem = Item::CreateItem(newItemEntryId, 1, 0); + if (!newItem) + continue; + if (!sT->CanTransmogrifyItemWithItem(player, oldItem->GetTemplate(), newItem->GetTemplate())) + continue; + if (sT->GetFakeEntry(oldItem->GetGUID()) == newItem->GetEntry()) + continue; + allowedItems.push_back(newItem); } - if (result) + for (uint32 i = startValue; i <= endValue; i++) { - std::vector allowedItems; - do { - uint32 newItemEntryId = (*result)[0].Get(); - Item* newItem = Item::CreateItem(newItemEntryId, 1, 0); - if (!newItem) - continue; - if (!sT->CanTransmogrifyItemWithItem(player, oldItem->GetTemplate(), newItem->GetTemplate())) - continue; - if (sT->GetFakeEntry(oldItem->GetGUID()) == newItem->GetEntry()) - continue; - allowedItems.push_back(newItem); - } while (result->NextRow()); - for (uint32 i = startValue; i <= endValue; i++) + if (allowedItems.empty() || i > allowedItems.size() - 1) { - if (allowedItems.empty() || i > allowedItems.size() - 1) - { - lastPage = true; - break; - } - Item* newItem = allowedItems.at(i); - AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(newItem->GetEntry(), 30, 30, -18, 0) + sT->GetItemLink(newItem, session), slot, newItem->GetEntry(), "Using this item for transmogrify will bind it to you and make it non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->GetItemIcon(newItem->GetEntry(), 40, 40, -15, -10) + sT->GetItemLink(newItem, session) + lineEnd, price, false); + lastPage = true; + break; } + Item* newItem = allowedItems.at(i); + AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(newItem->GetEntry(), 30, 30, -18, 0) + sT->GetItemLink(newItem, session), slot, newItem->GetEntry(), "Using this item for transmogrify will bind it to you and make it non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->GetItemIcon(newItem->GetEntry(), 40, 40, -15, -10) + sT->GetItemLink(newItem, session) + lineEnd, price, false); } - if (gossipPageNumber == EQUIPMENT_SLOT_END + 11) + } + if (gossipPageNumber == EQUIPMENT_SLOT_END + 11) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", EQUIPMENT_SLOT_END, slot); + if (!lastPage) { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", EQUIPMENT_SLOT_END, slot); - if (!lastPage) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot); - } + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot); } - else if (gossipPageNumber > EQUIPMENT_SLOT_END + 11) + } + else if (gossipPageNumber > EQUIPMENT_SLOT_END + 11) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", gossipPageNumber - 1, slot); + if (!lastPage) { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", gossipPageNumber - 1, slot); - if (!lastPage) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot); - } - } - else if (!lastPage) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", EQUIPMENT_SLOT_END + 11, slot); + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot); } + } + else if (!lastPage) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", EQUIPMENT_SLOT_END + 11, slot); + } - AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tRemove transmogrification", EQUIPMENT_SLOT_END + 3, slot, "Remove transmogrification from the slot?", 0, false); - AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", EQUIPMENT_SLOT_END, slot); - AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0); - SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); - })); + AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tRemove transmogrification", EQUIPMENT_SLOT_END + 3, slot, "Remove transmogrification from the slot?", 0, false); + AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", EQUIPMENT_SLOT_END, slot); + AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0); + SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); } else { @@ -476,6 +473,14 @@ public: class PS_Transmogrification : public PlayerScript { private: + void AddToDatabase(Player* player, Item* item) + { + if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE) || item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) + return; + ItemTemplate const* itemTemplate = item->GetTemplate(); + AddToDatabase(player, itemTemplate); + } + void AddToDatabase(Player* player, ItemTemplate const* itemTemplate) { if (!sT->GetTrackUnusableItems() && !sT->SuitableForTransmogrification(player, itemTemplate)) @@ -489,16 +494,12 @@ private: tempStream << std::hex << ItemQualityColors[itemTemplate->Quality]; std::string itemQuality = tempStream.str(); bool showChatMessage = !(player->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value); - std::string query = "SELECT account_id, item_template_id FROM custom_unlocked_appearances WHERE account_id = " + std::to_string(accountId) + " AND item_template_id = " + std::to_string(itemId); - player->GetSession()->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(query).WithCallback([=](QueryResult result) + if (sT->AddCollectedAppearance(accountId, itemId)) { - if (!result) - { - if (showChatMessage) - ChatHandler(player->GetSession()).PSendSysMessage( R"(|c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r has been added to your appearance collection.)", itemQuality.c_str(), itemId, itemName.c_str()); - CharacterDatabase.Execute( "INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId); - } - })); + if (showChatMessage) + ChatHandler(player->GetSession()).PSendSysMessage( R"(|c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r has been added to your appearance collection.)", itemQuality.c_str(), itemId, itemName.c_str()); + CharacterDatabase.Execute( "INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId); + } } public: PS_Transmogrification() : PlayerScript("Player_Transmogrify") { } @@ -507,8 +508,7 @@ public: { if (!sT->GetUseCollectionSystem()) return; - ItemTemplate const* pProto = it->GetTemplate(); - AddToDatabase(player, pProto); + AddToDatabase(player, it); } void OnLootItem(Player* player, Item* item, uint32 /*count*/, ObjectGuid /*lootguid*/) override @@ -517,7 +517,7 @@ public: return; if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound()) { - AddToDatabase(player, item->GetTemplate()); + AddToDatabase(player, item); } } @@ -527,7 +527,7 @@ public: return; if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound()) { - AddToDatabase(player, item->GetTemplate()); + AddToDatabase(player, item); } } @@ -537,7 +537,7 @@ public: return; if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound()) { - AddToDatabase(player, item->GetTemplate()); + AddToDatabase(player, item); } } @@ -637,6 +637,25 @@ public: void OnAfterConfigLoad(bool reload) override { sT->LoadConfig(reload); + if (sT->GetUseCollectionSystem()) + { + LOG_INFO("module", "Loading transmog appearance collection cache...."); + uint32 collectedAppearanceCount = 0; + QueryResult result = CharacterDatabase.Query("SELECT account_id, item_template_id FROM custom_unlocked_appearances"); + if (result) + { + do + { + uint32 accountId = (*result)[0].Get(); + uint32 itemId = (*result)[1].Get(); + if (sT->AddCollectedAppearance(accountId, itemId)) + { + collectedAppearanceCount++; + } + } while (result->NextRow()); + } + LOG_INFO("module", "Loaded {} collected appearances into cache", collectedAppearanceCount); + } } void OnStartup() override From 95d9af69dcfdd869e829dfd4831053e9fd138ed7 Mon Sep 17 00:00:00 2001 From: ZhengPeiRu21 <98835050+ZhengPeiRu21@users.noreply.github.com> Date: Mon, 23 May 2022 12:53:03 -0600 Subject: [PATCH 2/2] feat: Implement Hidden Transmog option (#79) --- conf/transmog.conf.dist | 6 ++++++ src/Transmogrification.cpp | 22 +++++++++++++++++++--- src/Transmogrification.h | 5 ++++- src/transmog_scripts.cpp | 16 +++++++++++++++- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/conf/transmog.conf.dist b/conf/transmog.conf.dist index 2232457..171f146 100644 --- a/conf/transmog.conf.dist +++ b/conf/transmog.conf.dist @@ -18,6 +18,11 @@ # If disabled, players must have an item in their bags to use as a transmogrification appearance source. # Default: 1 # +# Transmogrification.AllowHiddenTransmog +# Description: Enables/Disables the hiding of equipment through transmog +# If enabled, players can select an "invisible" appearance for items at the transmog vendor +# Default: 1 +# # Transmogrification.TrackUnusableItems # Description: If enabled, appearances are collected even for items that are not suitable for transmogrification. # This allows these appearances to be used later if the configuration is changed. @@ -43,6 +48,7 @@ Transmogrification.Enable = 1 Transmogrification.UseCollectionSystem = 1 +Transmogrification.AllowHiddenTransmog = 1 Transmogrification.TrackUnusableItems = 1 Transmogrification.EnableTransmogInfo = 1 diff --git a/src/Transmogrification.cpp b/src/Transmogrification.cpp index 250a8b6..ca2ee2b 100644 --- a/src/Transmogrification.cpp +++ b/src/Transmogrification.cpp @@ -310,8 +310,12 @@ bool Transmogrification::AddCollectedAppearance(uint32 accountId, uint32 itemId) } TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost) { + if (itemEntry == UINT_MAX) // Hidden transmog + { + return Transmogrify(player, nullptr, slot, no_cost, true); + } Item* itemTransmogrifier = Item::CreateItem(itemEntry, 1, 0); - return Transmogrify(player, itemTransmogrifier, slot, no_cost); + return Transmogrify(player, itemTransmogrifier, slot, no_cost, false); } TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost) { @@ -326,10 +330,10 @@ TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, ObjectGuid return LANG_ERR_TRANSMOG_MISSING_SRC_ITEM; } } - return Transmogrify(player, itemTransmogrifier, slot, no_cost); + return Transmogrify(player, itemTransmogrifier, slot, no_cost, false); } -TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost) +TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost, bool hidden_transmog) { int32 cost = 0; // slot of the transmogrified item @@ -347,6 +351,12 @@ TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, Item* item return LANG_ERR_TRANSMOG_MISSING_DEST_ITEM; } + if (hidden_transmog) + { + SetFakeEntry(player, HIDDEN_ITEM_ID, slot, itemTransmogrified); // newEntry + return LANG_ERR_TRANSMOG_OK; + } + if (!itemTransmogrifier) // reset look newEntry { // Custom @@ -689,6 +699,7 @@ void Transmogrification::LoadConfig(bool reload) IgnoreReqEvent = sConfigMgr->GetOption("Transmogrification.IgnoreReqEvent", false); IgnoreReqStats = sConfigMgr->GetOption("Transmogrification.IgnoreReqStats", false); UseCollectionSystem = sConfigMgr->GetOption("Transmogrification.UseCollectionSystem", true); + AllowHiddenTransmog = sConfigMgr->GetOption("Transmogrification.AllowHiddenTransmog", true); TrackUnusableItems = sConfigMgr->GetOption("Transmogrification.TrackUnusableItems", true); IsTransmogEnabled = sConfigMgr->GetOption("Transmogrification.Enable", true); @@ -764,6 +775,11 @@ bool Transmogrification::GetUseCollectionSystem() const return UseCollectionSystem; }; +bool Transmogrification::GetAllowHiddenTransmog() const +{ + return AllowHiddenTransmog; +} + bool Transmogrification::GetTrackUnusableItems() const { return TrackUnusableItems; diff --git a/src/Transmogrification.h b/src/Transmogrification.h index c96a813..1292450 100644 --- a/src/Transmogrification.h +++ b/src/Transmogrification.h @@ -16,6 +16,7 @@ #include #define PRESETS // comment this line to disable preset feature totally +#define HIDDEN_ITEM_ID 1 // used for hidden transmog - do not use a valid equipment ID #define MAX_OPTIONS 25 // do not alter class Item; @@ -127,6 +128,7 @@ public: bool IgnoreReqStats; bool UseCollectionSystem; + bool AllowHiddenTransmog; bool TrackUnusableItems; bool IsTransmogEnabled; @@ -151,7 +153,7 @@ public: TransmogAcoreStrings Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost = false); TransmogAcoreStrings Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost = false); - TransmogAcoreStrings Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost = false); + TransmogAcoreStrings Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost = false, bool hidden_transmog = false); bool CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* destination, ItemTemplate const* source) const; bool SuitableForTransmogrification(Player* player, ItemTemplate const* proto) const; // bool CanBeTransmogrified(Item const* item); @@ -176,6 +178,7 @@ public: uint32 GetSetNpcText() const; bool GetUseCollectionSystem() const; + bool GetAllowHiddenTransmog() const; bool GetTrackUnusableItems() const; [[nodiscard]] bool IsEnabled() const; }; diff --git a/src/transmog_scripts.cpp b/src/transmog_scripts.cpp index a80a9be..4d54c0f 100644 --- a/src/transmog_scripts.cpp +++ b/src/transmog_scripts.cpp @@ -373,6 +373,20 @@ public: if (sT->collectionCache.find(accountId) != sT->collectionCache.end()) { std::vector allowedItems; + if (sT->GetAllowHiddenTransmog()) + { + // Offset the start and end values to make space for invisible item entry + endValue--; + if (pageNumber != 0) + { + startValue--; + } + else + { + // Add invisible item entry + AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "Hide Slot", slot, UINT_MAX, "You are hiding the item in this slot.\nDo you wish to continue?\n\n" + lineEnd, 0, false); + } + } for (uint32 newItemEntryId : sT->collectionCache[accountId]) { Item* newItem = Item::CreateItem(newItemEntryId, 1, 0); if (!newItem) @@ -590,7 +604,7 @@ public: { ObjectGuid itemGUID = ObjectGuid::Create((*result)[0].Get()); uint32 fakeEntry = (*result)[1].Get(); - if (sObjectMgr->GetItemTemplate(fakeEntry)) + if (fakeEntry == HIDDEN_ITEM_ID || sObjectMgr->GetItemTemplate(fakeEntry)) { sT->dataMap[itemGUID] = playerGUID; sT->entryMap[playerGUID][itemGUID] = fakeEntry;