azerothcore / azerothcore-wotlk

Complete Open Source and Modular solution for MMO
http://www.azerothcore.org
GNU Affero General Public License v3.0
6.41k stars 2.57k forks source link

Suggestion: add in core SendListInventory for player/item gossip #4236

Open r0m1ntik opened 3 years ago

r0m1ntik commented 3 years ago

For example, for modules that use player scripts or item scripts:

player->GetSession()->SendListInventory(player->GetGUID(), VENDOR_ENTRY);

As for me, works like multivendor

I have an example of how to implement, done for Trinity 2018

--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -21480,9 +21480,12 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c
         EquipNewItem(uiDest, item, true);
     if (it)
     {
-        uint32 new_count = pVendor->UpdateVendorItemCurrentCount(crItem, pProto->BuyCount * count);
+       
+        uint32 new_count = 0xFFFFFFFF;
+       if(pVendor)
+           pVendor->UpdateVendorItemCurrentCount(crItem, pProto->BuyCount * count);
         WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
-        data << uint64(pVendor->GetGUID());
+        data << uint64(pVendor ? pVendor->GetGUID() : GetGUID());
         data << uint32(vendorslot + 1);                   // numbered from 1 at client
         data << int32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
         data << uint32(count);
@@ -21533,16 +21536,20 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin

     if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAG2_FACTION_HORDE && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAG2_FACTION_ALLIANCE && GetTeam() == HORDE)))
         return false;
-
-    Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
-    if (!creature)
-    {
-        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;
-    }
-
+   Creature* creature = NULL;
+    uint32 currentVendor = GetSession()->GetCurrentVendor();
+   if(currentVendor == 0)
+   {
+       creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
+       if (!creature)
+       {
+           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(creature)
     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)",
@@ -21550,12 +21557,10 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
         SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
         return false;
     }
-
-    uint32 currentVendor = GetSession()->GetCurrentVendor();
-    if (currentVendor && vendorguid != PlayerTalkClass->GetGossipMenu().GetSenderGUID())
+    if (!creature && currentVendor && vendorguid != PlayerTalkClass->GetGossipMenu().GetSenderGUID())
         return false; // Cheating

-    VendorItemData const* vItems = currentVendor ? sObjectMgr->GetNpcVendorItemList(currentVendor) : creature->GetVendorItems();
+    VendorItemData const* vItems = creature ? creature->GetVendorItems() : sObjectMgr->GetNpcVendorItemList(currentVendor);
     if (!vItems || vItems->Empty())
     {
         SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
@@ -21577,15 +21582,18 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
     }

     // check current item amount if it limited
-    if (crItem->maxcount != 0)
-    {
-        if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
-        {
-            SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
-            return false;
-        }
-    }
-
+   if(currentVendor == 0)
+   {
+       if (crItem->maxcount != 0)
+       {
+           if(creature)
+               if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
+               {
+                   SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
+                   return false;
+               }
+       }
+   }
     if (pProto->RequiredReputationFaction && (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank))
     {
         SendBuyError(BUY_ERR_REPUTATION_REQUIRE, creature, item, 0);
@@ -21647,7 +21655,8 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
         price = pProto->BuyPrice * count; //it should not exceed MAX_MONEY_AMOUNT

         // reputation discount
-        price = uint32(floor(price * GetReputationPriceDiscount(creature)));
+       if(creature)
+           price = uint32(floor(price * GetReputationPriceDiscount(creature)));

         if (!HasEnoughMoney(price))
         {
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 54de6eb..85eaafb 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -608,8 +608,10 @@ void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData)

 void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
 {
-    Creature* vendor = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
-    if (!vendor)
+    Creature* vendor = NULL;
+   if(!vendorGuid.IsPlayer())
+       vendor = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
+    if (!vendor && vendorEntry == 0)
     {
         TC_LOG_DEBUG("network", "WORLD: SendListInventory - %s not found or you can not interact with him.", vendorGuid.ToString().c_str());
         _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty, 0);
@@ -621,9 +623,11 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
         GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);

     // Stop the npc if moving
-    vendor->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
-    vendor->SetHomePosition(vendor->GetPosition());
-
+   if(vendor)
+   {
+       vendor->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
+       vendor->SetHomePosition(vendor->GetPosition());
+   }
     SetCurrentVendor(vendorEntry);

     VendorItemData const* items = vendorEntry ? sObjectMgr->GetNpcVendorItemList(vendorEntry) : vendor->GetVendorItems();
@@ -646,7 +650,7 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
     size_t countPos = data.wpos();
     data << uint8(count);

-    float discountMod = _player->GetReputationPriceDiscount(vendor);
+    float discountMod = vendor ? _player->GetReputationPriceDiscount(vendor) : 1.0f;

     for (uint8 slot = 0; slot < itemCount; ++slot)
     {
@@ -663,16 +667,17 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)

                 // Items sold out are not displayed in list

-                uint32 leftInStock = !item->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(item);
+                uint32 leftInStock = !item->maxcount ? 0xFFFFFFFF : (vendor ? vendor->GetVendorItemCurrentCount(item) : 0xFFFFFFFF);
                 if (!_player->IsGameMaster() && !leftInStock)
                     continue;
- 
-                if (!sConditionMgr->IsObjectMeetingVendorItemConditions(vendor->GetEntry(), item->item, _player, vendor))
-                {
-                    TC_LOG_DEBUG("condition", "SendListInventory: conditions not met for creature entry %u item %u", vendor->GetEntry(), item->item);
-                    continue;                                         
-                }
-
+               if(vendor)
+               {
+                   if (!sConditionMgr->IsObjectMeetingVendorItemConditions(vendor->GetEntry(), item->item, _player, vendor))
+                   {
+                       TC_LOG_DEBUG("condition", "SendListInventory: conditions not met for creature entry %u item %u", vendor->GetEntry(), item->item);
+                       continue;                                         
+                   }
+               }
                 // reputation discount
                 int32 price = item->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0;

-- 
2.10.5

I think this patch will open up many other possibilities for custom patches

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/95786137-suggestion-add-in-core-sendlistinventory-for-player-item-gossip?utm_campaign=plugin&utm_content=tracker%2F40032087&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F40032087&utm_medium=issues&utm_source=github).
FrancescoBorzi commented 3 years ago

I think this patch will open up many other possibilities for custom patches

@r0m1ntik as long as it doesn't break (or alter in any way) the default behaviour, any changes in this direction is welcome

PS: I haven't reviewed the code (I will wait a PR to do that), just commenting on the idea in general of adding stuff to the core that allows further customisations

r0m1ntik commented 3 years ago

@FrancescoBorzi According to Idea, nothing changes there, just in some cases I add a check IsAnyTypeCreature() or _IS_CRE_OR_VEH_OR_PET_GUID()_ for example