[Trinity] Multivendor menu

User Tag List

Results 1 to 1 of 1
  1. #1
    _Hkarta's Avatar Corporal
    Reputation
    46
    Join Date
    Apr 2010
    Posts
    20
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [Trinity] Multivendor

    [C++, SQL] Multivendor


    Hello there,

    Id like to present you one of scripts from my private collection i have decided to make public.
    Long time ago, I used to be administrator of the biggest czech BG/Arena server. During that time, i often questioned myself - where to put all those f*cking vendors?
    Well, this script is the final solution of that question.

    It combines multiple vendors into single one, which, as far as i know, was impossible on trinity, unless the included vendors were nearby.

    There is only one problem - all of your generic vendors have to have simple script attached (which is same for all of them) and gossip flag added. It could be done with single sql query, so it shouldnt be much of problem.

    Lets get started:
    • Create npc with npcflag 129 and set scriptname to vendor_multi
    • Set all generic vendors flag to 129 and scriptname to vendor_general
    • Know how to add creature scripts to ScriptLoaderu


    Sql - import to world db, ill explain later
    Code:
    CREATE TABLE `_customvendors` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `vendor` int(11) DEFAULT NULL,
      `group` int(11) DEFAULT NULL,
      `next` int(11) DEFAULT NULL,
      `desc` text,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1;

    C++ - lines with + are to be added (without + )

    Player.h
    Code:
    class Player : public Unit, public GridObject<Player>
    {
        friend class WorldSession;
        friend void Item::AddToUpdateQueueOf(Player* player);
        friend void Item::RemoveFromUpdateQueueOf(Player* player);
        public:
    +       int currentVendorEntry;

    Player.cpp
    Code:
    Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_reputationMgr(this)
    {
        m_speakTime = 0;
        m_speakCount = 0;
    +  currentVendorEntry = -1;
    now edit the function bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot) like this. Dont have the original code, sorry
    Code:
    bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot)
    {
        // cheating attempt
        if (count < 1) count = 1;
    
        // cheating attempt
        if (slot > MAX_BAG_SIZE && slot !=NULL_SLOT)
            return false;
    
        if (!isAlive())
            return false;
    
        ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item);
        if (!pProto)
        {
            SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
            return false;
        }
    
        Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
        if (!creature)
        {
            sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)));
            SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
            return false;
        }
    
    +  VendorItemData const* vItems;
    + if(currentVendorEntry != -1)
    +      vItems = sObjectMgr->GetNpcVendorItemList(currentVendorEntry); // creature->GetVendorItems();
    + else
    +      vItems = creature->GetVendorItems();
    
        if (!vItems || vItems->Empty())
        {
    and in the same function edit
    Code:
        if (crItem->maxcount != 0)
        {
            if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
            {
                SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
                return false;
            }
        }
    like this
    Code:
        if (crItem->maxcount != 0 && currentVendorEntry == -1)
        {
            if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
            {
                SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
                return false;
            }
        }
    Worldsession.h
    Code:
    void SendNameQueryOpcode(uint64 guid);
    
            void SendTrainerList(uint64 guid);
            void SendTrainerList(uint64 guid, const std::string& strTitle);
            void SendListInventory(uint64 guid);
    +      void SendListInventory(uint64 vendorGuid, int entry);
    ItemHandler.cpp
    after function void WorldSession::SendListInventory(uint64 vendorGuid) add
    Code:
    void WorldSession::SendListInventory(uint64 vendorGuid, int entry)
    {
        sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_LIST_INVENTORY");
    
        Creature* vendor = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
        if (!vendor)
        {
            sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendListInventory - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(vendorGuid)));
            _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
            return;
        }
    
        // remove fake death
        if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
            GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
    
        // Stop the npc if moving
        if (vendor->HasUnitState(UNIT_STATE_MOVING))
            vendor->StopMoving();
    
        VendorItemData const* items = sObjectMgr->GetNpcVendorItemList(entry);//vendor->GetVendorItems();
    
        if (!items)
        {
            WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + 1);
            data << uint64(vendorGuid);
            data << uint8(0);                                   // count == 0, next will be error code
            data << uint8(0);                                   // "Vendor has no inventory"
            SendPacket(&data);
            return;
        }
    
        uint8 itemCount = items->GetItemCount();
        uint8 count = 0;
    
        WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4);
        data << uint64(vendorGuid);
    
        size_t countPos = data.wpos();
        data << uint8(count);
    
        float discountMod = _player->GetReputationPriceDiscount(vendor);
    
        for (uint8 slot = 0; slot < itemCount; ++slot)
        {
            if (VendorItem const* item = items->GetItem(slot))
            {
                if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(item->item))
                {
                    if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
                        continue;
                    // Only display items in vendor lists for the team the
                    // player is on. If GM on, display all items.
                    if (!_player->isGameMaster() && ((itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && _player->GetTeam() == ALLIANCE) || (itemTemplate->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && _player->GetTeam() == HORDE)))
                        continue;
    
                    // Items sold out are not displayed in list
                    uint32 leftInStock = !item->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(item);
                    if (!_player->isGameMaster() && !leftInStock)
                        continue;
    
                    ++count;
    
                    // reputation discount
                    int32 price = item->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0;
    
                    data << uint32(slot + 1);       // client expects counting to start at 1
                    data << uint32(item->item);
                    data << uint32(itemTemplate->DisplayInfoID);
                    data << int32(leftInStock);
                    data << uint32(price);
                    data << uint32(itemTemplate->MaxDurability);
                    data << uint32(itemTemplate->BuyCount);
                    data << uint32(item->ExtendedCost);
                }
            }
        }
    
        if (count == 0)
        {
            data << uint8(0);
            SendPacket(&data);
            return;
        }
    
        data.put<uint8>(countPos, count);
        SendPacket(&data);
    }

    ScriptMgr.cpp
    Code:
    #include "ScriptPCH.h"
    #include "ScriptMgr.h"
    #include "Config.h"
    #include "DatabaseEnv.h"
    #include "DBCStores.h"
    #include "ObjectMgr.h"
    #include "OutdoorPvPMgr.h"
    #include "ScriptLoader.h"
    #include "ScriptSystem.h"
    #include "Transport.h"
    #include "Vehicle.h"
    + #include "CustomVendor.h"
    Code:
    sLog->outString(">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
    
    + oldMSTime = getMSTime();
    + sLog->outString("Loading custom vendors");
    + sLog->outString(">> Loaded %u custom vendor catageory entries in %u ms", CustomVendorMgr.LoadVendors(), GetMSTimeDiffToNow(oldMSTime));
    + sLog->outString();

    Now, add these scripts to core (game project)

    CustomVendor.h
    Code:
    #ifndef CUSTOMVENDOR_H
    #define CUSTOMVENDOR_H
    extern WorldDatabaseWorkerPool WorldDatabase;
    
    class VendorEntry
    {
    public:
    	int id, vendor, group, next;
    	std::string desc;
    	VendorEntry(int, int, int, int, std::string);
    };
    
    typedef std::list<VendorEntry *> VendorEntryList;
    
    class CustomVendor
    {
    public:
    	VendorEntryList vendorEntryList;
    	CustomVendor(void);
    	~CustomVendor(void);
    	int LoadVendors(void);
    	VendorEntryList* GetItemsForEntry(int, int);
    	VendorEntryList* GetBaseItemsForEntry(int);
    	int GetGroup(int);
    	int GetNext(int, int);
    	VendorEntry* GetParent(int);
    };
    
    extern CustomVendor CustomVendorMgr;
    
    #endif
    And CustomVendor.cpp
    Code:
    #include "CustomVendor.h"
    #include "gamePCH.h"
    #include <stdio.h>
    #include <string>
    #include <list>
    
    CustomVendor CustomVendorMgr;
    
    CustomVendor::CustomVendor(void)
    {
    }
    
    
    CustomVendor::~CustomVendor(void)
    {
    }
    
    int CustomVendor::LoadVendors(void)
    {
    	vendorEntryList.clear();
    	int i = 0;
    	QueryResult result = WorldDatabase.PQuery("SELECT `id`, `vendor`, `group`, `next`, `desc` FROM _customvendors");
    	//												  0	  1		  2		 3	   4   
    	if (result)
    	{
    		do
    		{
    			Field *fields = result->Fetch();
    			int id = fields[0].GetInt32();
    			int vendor = fields[1].GetInt32();
    			int group = fields[2].GetInt32();
    			int next = fields[3].GetInt32();
    			std::string desc = fields[4].GetString();
    			vendorEntryList.push_back(new VendorEntry(id, vendor, group, next, desc));
    			i++;
    		}
        	while (result->NextRow());
    	}
    
    	return i;
    }
    
    VendorEntryList* CustomVendor::GetItemsForEntry(int entry, int id)
    {
    	VendorEntryList *result = new VendorEntryList();
    	VendorEntryList::iterator i;
    	int group = GetGroup(id);
    	for (i = vendorEntryList.begin(); i != vendorEntryList.end(); ++i)
    	{
    		VendorEntry *vendorEntry = *i;
    		if(vendorEntry->group == group && vendorEntry->vendor == entry)
    			result->push_back(vendorEntry);
    	}
    	return result;
    }
    
    VendorEntryList* CustomVendor::GetBaseItemsForEntry(int entry)
    {
    	VendorEntryList *result = new VendorEntryList();
    	VendorEntryList::iterator i;
    	for (i = vendorEntryList.begin(); i != vendorEntryList.end(); ++i)
    	{
    		VendorEntry *vendorEntry = *i;
    		if(vendorEntry->group == 0 && vendorEntry->vendor == entry)
    			result->push_back(vendorEntry);
    	}
    	return result;
    }
    
    int CustomVendor::GetGroup(int id)
    {
    	VendorEntryList::iterator i;
    	for (i = vendorEntryList.begin(); i != vendorEntryList.end(); ++i)
    	{
    		VendorEntry *vendorEntry = *i;
    		if(vendorEntry->id == id)
    			return vendorEntry->group;
    	}
    	return -1;
    }
    
    int CustomVendor::GetNext(int entry, int id)
    {
    	VendorEntryList::iterator i;
    	for (i = vendorEntryList.begin(); i != vendorEntryList.end(); ++i)
    	{
    		VendorEntry *vendorEntry = *i;
    		if(vendorEntry->id == id && vendorEntry->vendor == entry)
    			return vendorEntry->next;
    	}
    	return -1;
    }
    
    VendorEntry* CustomVendor::GetParent(int id)
    {
    	VendorEntryList::iterator i;
    	for (i = vendorEntryList.begin(); i != vendorEntryList.end(); ++i)
    	{
    		VendorEntry *vendorEntry = *i;
    		if(vendorEntry->next == id)
    			return vendorEntry;
    	}
    
    	return NULL;
    }
    
    VendorEntry::VendorEntry(int id, int vendor, int group, int next, std::string desc)
    {
    	this->id = id;
    	this->vendor = vendor;
    	this->group = group;
    	this->next = next;
    	this->desc = desc;
    }
    And finaly, npc scripts:

    vendor_multi.cpp
    Code:
    #include "ScriptPCH.h"
    #include <cstring>
    #include "../CustomVendor.h" // upravte si cestu
    
    class vendor_multi : public CreatureScript
    {
        public:
    
            vendor_multi()
                : CreatureScript("vendor_multi")
            {
            }
    
            bool OnGossipHello(Player* player, Creature* creature)
            {
                IterateCategory(player, creature);
                return true;
            }
    
            void IterateCategory(Player* player, Creature* creature, int category = 0)
            {
                player->PlayerTalkClass->ClearMenus();
                player->currentVendorEntry = -1;
                VendorEntry *current = current = CustomVendorMgr.GetParent(category);
                VendorEntryList *result;
                
                if(category == 0)
                    result = CustomVendorMgr.GetBaseItemsForEntry(creature->GetCreatureInfo()->Entry);
                else
                    result = CustomVendorMgr.GetItemsForEntry(creature->GetCreatureInfo()->Entry, category);
                VendorEntryList::iterator i;
    
                int x = 0;
                for (i = result->begin(); i != result->end(); ++i)
                {
                    VendorEntry *vendorEntry = *i;
    
                    int icon = GOSSIP_ICON_CHAT;
                    if(CustomVendorMgr.GetNext(creature->GetCreatureInfo()->Entry, vendorEntry->next) < 0)
                        icon = GOSSIP_ICON_MONEY_BAG;
    
                    player->ADD_GOSSIP_ITEM(icon, vendorEntry->desc, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2+vendorEntry->next);
                    x++;
                    if(x == 5)
                    {
                        player->PlayerTalkClass->SendGossipMenu(907, creature->GetGUID());
                        x = 0;
                    }
                }
    
                if(current)
                {
                    if(current->group != 0)
                        player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "<< Zpet", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2+current->id);
                    else
                        player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "<< Zpet", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
                    x++;
                }
    
                if(x != 0)
                    player->PlayerTalkClass->SendGossipMenu(907, creature->GetGUID());
            }
    
            bool OnGossipSelect(Player* player, Creature* creature, uint32 /*uiSender*/, uint32 uiAction)
            {
                if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
                {
                    IterateCategory(player, creature);
                }
                else if (uiAction > GOSSIP_ACTION_INFO_DEF+1)
                {
                    int id = uiAction-GOSSIP_ACTION_INFO_DEF-2;
                    int next = CustomVendorMgr.GetNext(creature->GetCreatureInfo()->Entry, id);
                    if(next < 0)
                    {
                        next = (-1) * next;
                        player->currentVendorEntry = next;
                        player->GetSession()->SendListInventory(creature->GetGUID(), next);
                    }
                    else
                        IterateCategory(player, creature, id);
                }
    
                return true;
            }
    
    };
    
    void AddSC_vendor_multi()
    {
        new vendor_multi();
    }

    a vendor_general.cpp
    Code:
    #include "ScriptPCH.h"
    #include <cstring>
    class vendor_general : public CreatureScript
    {
        public:
            vendor_general()
                : CreatureScript("vendor_general")
            {
            }
            bool OnGossipHello(Player* player, Creature* creature)
            {
       player->currentVendorEntry = -1;
                player->GetSession()->SendListInventory(creature->GetGUID());
                return true;
            }
    };
    void AddSC_vendor_general()
    {
        new vendor_general();
    }
    And now ill briefly explain db part; its a bit impractical, but i was layz to remade it once i found how hard it is to edit it :P

    `vendor` - entry npc that should diplay this record
    `group` - all records that should be shown ingame on the same page should have same group
    `next` - next id (ID from this table) of record, that group that we eant to show
    `desc` - text, který se zobrazí

    Vendors used for npc in video
    Code:
    Entry Name  Subname
    17     One handed Axe 1     Weapon One hand
    18     Two handed Axe 1     Weapon Two-Hand
    19     Bow 1     Weapon Ranged
    20     Gun 1     Weapon Ranged
    21     One handed Mace 1     Weapon One hand
    22     One handed Mace 1     Weapon Main hand
    23     Two handed Mace 1     Weapon Two-Hand
    24     Polearm 1     Weapon Two-Hand
    25     One handed Sword 1     Weapon One hand
    26     One handed Sword 1     Weapon Main hand
    27     Two handed Sword 1     Weapon Two-Hand
    28     Staff 1     Weapon Two-Hand
    29     Fist Weapon 1     Weapon Main hand
    30     Fist Weapon 1     Weapon Off hand
    31     Dagger 1     Weapon One hand
    32     Dagger 1     Weapon Main hand
    33     Thrown 1     Weapon Thrown
    34     Crossbow 1     Weapon Ranged
    35     Wand 1     Weapon Ranged right
    and i filled the custom table like this
    Code:
    id vendor group   next        desc
    58 215 1000 -35 Buy: wand
    57 215 1000 -33 Buy: thrown
    56 215 1000 -34 Buy: crossbow
    55 215 1000 -20 Buy: gun
    54 215 1000 -19 Buy: bow
    53 215 3 58 Wands
    52 215 3 57 Thrown
    51 215 3 56 Crossbows
    50 215 3 55 Guns
    49 215 3 54 Bows
    48 215 1000 -24 Buy: polearm
    47 215 1000 -28 Buy: Staff
    46 215 1000 -27 Buy: 2h sword
    45 215 1000 -23 Buy: 2h mace
    44 215 1000 -18 Buy: 2H axe
    43 215 2 48 Polearms
    42 215 2 47 Staves
    36 215 2 46 Swords
    35 215 2 45 Maces
    34 215 2 44 Axes
    33 215 1000 -30 Buy: Off hand fist weapon
    32 215 1000 -29 Buy: Main hand fist weapon
    31 215 1000 -25 Buy: One hand sword
    30 215 1000 -26 Buy: Main hand sword
    29 215 1000 -21 Buy: One hand mace
    28 215 1000 -22 Buy: Main hand mace
    27 215 14 33 Off hand Fist weapon
    26 215 14 32 Main hand Fist weapon
    25 215 13 31 One hand Sword
    24 215 13 30 Main hand Sword
    23 215 12 29 One hand Mace
    22 215 12 28 Main hand Mace
    21 215 1000 -17 Buy: One hand Axe
    41 215 1000 -31 Buy: One hand Dagger
    40 215 1000 -32 Buy: Main hand Dagger
    39 215 15 41 One hand Dagger
    38 215 15 40 Main hand Dagger
    37 215 1 38 Daggers
    19 215 1 26 Fist weapons
    18 215 1 24 Swords
    17 215 1 22 Maces
    16 215 1 21 Axes
    15 215 0 49 Ranged
    14 215 0 34 Two handed
    13 215 0 16 One handed
    Records shown on first page in npc should always have group 0!
    To make it easier readable, i use group 1000 for every record that displays vendor content. When next is set to negative value, it means that vendor with entry equal to next * (-1) should be opened.
    Lets look at this example. Lets say i want to buy Dagger.

    So, the first record is
    Code:
     13    215    0    16    One handed
    group 0, next is id 16
    Code:
    16 215 1 21 Axes
    there are these records with the same group
    Code:
    37 215 1 38 Daggers
    19 215 1 26 Fist weapons
    18 215 1 24 Swords
    17 215 1 22 Maces
    16 215 1 21 Axes
    because of that, when i click on One handed in game, it shows a list with Daggers, Fist weapons, Swords, Axes - you would see tha same list if id of next record is set to 18 (Swords), because they all share the same group (1)
    Clicking on daggers produces following list
    Code:
    39    215    15    41    One hand Dagger
    38    215    15    40    Main hand Dagger
    (because record with id 38 has got group 15, it would work as well if next is set to 39) - i selected main hand, record with id 40 is shown
    Code:
    40 215 1000 -32 Buy: Main hand Dagger
    this record got its next value equal to (-32) - thats why no list is shown, but vendor with entry 32 is shown.

    Hopefuly its understandable.

    [Trinity] Multivendor

Similar Threads

  1. Enjayce's Ultimate 3.1.3 Trinity Repack
    By enjayce in forum WoW EMU General Releases
    Replies: 617
    Last Post: 01-22-2010, 08:46 PM
  2. [Release]sve3N's Trinity Repack
    By sve3nn in forum WoW EMU General Releases
    Replies: 12
    Last Post: 08-11-2009, 09:33 AM
  3. Vote and reward system - Mangos/trinity - Paying $200
    By insainz in forum WoW EMU Questions & Requests
    Replies: 19
    Last Post: 04-29-2009, 12:33 PM
  4. [Release]Trinity TBC 2.4.3 Server work 100%
    By KrisBG in forum WoW EMU General Releases
    Replies: 17
    Last Post: 01-31-2009, 05:26 PM
  5. [Info] Code Trinity
    By AzolexX in forum World of Warcraft Emulator Servers
    Replies: 5
    Last Post: 09-27-2008, 01:51 PM
All times are GMT -5. The time now is 11:41 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search