Go Back   EQEmulator Home > EQEmulator Forums > Archives > Archive::Development > Archive::Development

Archive::Development Archive area for Development's posts that were moved here after an inactivity period of 90 days.

Reply
 
Thread Tools Display Modes
  #1  
Old 01-28-2002, 11:59 AM
darvik
Fire Beetle
 
Join Date: Jan 2002
Posts: 21
Default Shopkeeper Code

Been doing some adds to the code - based off 0.2.0 , added lots of stuff for merchant interaction, but it's far from complete. I coulnd't figure out how to do the database stuff right, so basicly the item list is hardcoded for all merchants, they all sell the same 2 items, and no matter what you buy, you get grapes. You can sell stuff and get money though.. and the client no longer locks up when you try to interact with a merchant.
Had to figure out a lot of this stuff out on my own because nobody else has done it before.. the opCodes were not even in showEQ.

Anyway - someone else will have to do the database portion of this -- I also included some sample sql of how this can be done without having to modify the npc_types table.

Have to split code into seperate posts ;/

code for eq_opcodes.h
Code:
// Darvik: for shopkeepers
#define OP_ShopRequest     0x0b20 // right-click on merchant
#define OP_ShopItem        0x0c20
#define OP_ShopWelcome     0x1420
#define OP_ShopPlayerBuy   0x3520
#define OP_ShopGiveItem    0x3120
#define OP_ShopTakeMoney   0x3520
#define OP_ShopPlayerSell  0x2720
#define OP_ShopEnd         0x3720
#define OP_ShopEndConfirm  0x4521
code for client_process.cpp - I added this just above case OP_Jump:
Code:
case OP_ShopRequest:
{
	Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer;
	if (app->size == sizeof(Merchant_Click_Struct))
	{
		cout << name << " is at merchant. ID:" << mc->merchantid << endl;
		DumpPacketHex(app);
		int32 mi = mc->merchantid;

		// Send back opcode OP_ShopRequest - tells client to open merchant window.
		APPLAYER* outapp = new APPLAYER;
		outapp->opcode = OP_ShopRequest;
		outapp->size = sizeof(Merchant_Click_Struct);
		outapp->pBuffer = new uchar[sizeof(Merchant_Click_Struct)];
		Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
		mco->mcs_unknown001 = mc->mcs_unknown001;
		mco->merchantid = mc->merchantid;
		mco->itemslot = 0x01;			// unused here, but we set to a harmless value.
		mco->mcs_unknown002[0] = mc->mcs_unknown002[0];
		mco->mcs_unknown002[1] = mc->mcs_unknown002[0];
		mco->mcs_unknown002[2] = mc->mcs_unknown002[0];
		mco->quantity = 0x1b;			// qty when used in purchase, unused here.
		mco->mcs_unknown003[0] = 0x0d;	// unknown data - has something to do with
		mco->mcs_unknown003[1] = 0xa0;	// buying and selling cost or markup.
		mco->mcs_unknown003[2] = 0x3F;	// <- changing this value even slightly severly screws up price.
		cout << "sending back merchant_click_struct to client." << endl;
		DumpPacketHex(outapp);

		QueuePacket(outapp);
		delete outapp;
							
		// TODO: Lookup	merchants wares..
		// TODO: Start Loop through wares..
			// Debug - hardcoded short beer .. trying to get stackable items to work.
			uint16 item_nr = 16592;
			Item_Struct* item = database.GetItem(item_nr);
			cout << "merchant is selling: " << item->name << endl;
			// Send this item to the client.
			outapp = new APPLAYER;
			outapp->opcode = OP_ShopItem;
			outapp->size = sizeof(Item_Shop_Struct);
			outapp->pBuffer = new uchar[outapp->size];
			Item_Shop_Struct* iss = (Item_Shop_Struct*)outapp->pBuffer;
			iss->merchantid = mi;
			iss->itemtype = 0x00;			// TODO: needs to be parsed from item.
			item->equipSlot = 0x00;			// this needs to be incremented in loop.
			memcpy(&iss->item, item, sizeof(Item_Struct));
			iss->iss_unknown001 = 0;
			QueuePacket(outapp);
			delete outapp;


			// Debug - hardcoded cloth cap..
			item_nr = 1001;
			item = database.GetItem(item_nr);
			cout << "merchant is selling: " << item->name << endl;
			// Send this item to the client.
			outapp = new APPLAYER;
			outapp->opcode = OP_ShopItem;
			outapp->size = sizeof(Item_Shop_Struct);
			outapp->pBuffer = new uchar[outapp->size];
			iss = (Item_Shop_Struct*)outapp->pBuffer;
			iss->merchantid = mi;
			iss->itemtype = 0x01;			// TODO: needs to be parsed from item.
			item->equipSlot = 0x01;			// this needs to be incremented in loop.
			item->common.number = 1;		// not sure if this tells how many the merchant has.
			memcpy(&iss->item, item, sizeof(Item_Struct));
			iss->iss_unknown001 = 0;
			QueuePacket(outapp);
			delete outapp;

		// TODO: End Loop through wares..

		// say welcome to player - dont know c well enough to do this right.
		// it's just a single null terminated string, heh.
		outapp = new APPLAYER;
		outapp->opcode = OP_ShopWelcome;
		outapp->pBuffer = new uchar[12];
		outapp->size = 12;
		outapp->pBuffer[0] = 0x48;
		outapp->pBuffer[1] = 0x65;
		outapp->pBuffer[2] = 0x6c;
		outapp->pBuffer[3] = 0x6c;
		outapp->pBuffer[4] = 0x6f;
		outapp->pBuffer[5] = 0x20;
		outapp->pBuffer[6] = 0x74;
		outapp->pBuffer[7] = 0x68;
		outapp->pBuffer[8] = 0x65;
		outapp->pBuffer[9] = 0x72;
		outapp->pBuffer[10] = 0x65;
		outapp->pBuffer[11] = 0x00;	
		QueuePacket(outapp);
		delete outapp;

	}
	else
	{
		cout << "Wrong size" << app->size  << ", should be " << sizeof(Merchant_Click_Struct) << endl;
	}
	break;
}
Will follow this post with the rest of the opCodes..
Reply With Quote
  #2  
Old 01-28-2002, 12:03 PM
darvik
Fire Beetle
 
Join Date: Jan 2002
Posts: 21
Default

Rest of the OpCodes , the structs, and the SQL..

Yes, code is a little sloppy, can be cleaned up a bit when databse stuff is implemented

Code:
case OP_ShopPlayerBuy:
{
	cout << name << " is attempting to purchase an item..  " << endl;
	Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer;
	// this item number needs to bounce back to the merchant table, since the client
	// only tells us what slot the client is wanting to purchase.
	// slot value is in mp->itemslot.
	uint16 item_nr = 16592;	// hardcoded to always sell you grapes :)
	Item_Struct* item = database.GetItem(item_nr);
						
	// Give Item
	APPLAYER* outapp = new APPLAYER;
	outapp->opcode = OP_ShopGiveItem;
	outapp->size = sizeof(Item_Struct);
	outapp->pBuffer = new uchar[outapp->size];
	item->common.number = mp->merchant.quantity;		// number purchased from merchant.

	// copied this code from loot code - but doesn't seem to work right.
	item->equipSlot = 0xFF;
	for (int i=22; i<31; i++)
	{
		if (pp.inventory[i] == 0xFFFF || pp.inventory[i] == item->item_nr)
		{
			item->equipSlot = i;
			break;
		}
	}
	if (item->equipSlot == 0xFF)
	{
		Message(14,"There is no more room in your inventory. Item cannot be purchased.");
							delete outapp;
	}
	else
	{
		//item->equipSlot = 0x1d;
		memcpy(outapp->pBuffer, item, sizeof(Item_Struct));
		QueuePacket(outapp);
		delete outapp;

		// Take Players Money
		outapp = new APPLAYER;
		outapp->opcode = OP_ShopTakeMoney;
		outapp->size = sizeof(Merchant_Purchase_Struct);
		outapp->pBuffer = new uchar[outapp->size];
		Merchant_Purchase_Struct* mps = (Merchant_Purchase_Struct*)outapp->pBuffer;
		mps->merchant.mcs_unknown001 = mp->merchant.mcs_unknown001;
		mps->merchant.merchantid = mp->merchant.merchantid;
		mps->merchant.itemslot = mp->merchant.itemslot;
		mps->merchant.mcs_unknown002[0] = 0x00;
		mps->merchant.mcs_unknown002[1] = 0x00;
		mps->merchant.mcs_unknown002[2] = 0x00;
		mps->merchant.quantity = mp->merchant.quantity;
		mps->merchant.mcs_unknown003[0] = 0x00;
		mps->merchant.mcs_unknown003[1] = 0x00;
		mps->merchant.mcs_unknown003[2] = 0x00;
		mps->mps_unknown001 = item->cost;			// amount in copper.

		cout << "taking " << item->cost << " copper from " << name << "." << endl;
		DumpPacketHex(outapp);
		QueuePacket(outapp);
		delete outapp;
	}
	break;
}

case OP_ShopPlayerSell:
{
	cout << name << " is trying to sell an item." << endl;
	DumpPacketHex(app);

	Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer;
	APPLAYER* outapp = new APPLAYER;
	outapp->opcode = OP_ShopPlayerSell;
	outapp->size = sizeof(Merchant_Purchase_Struct);
	outapp->pBuffer = new uchar[outapp->size];
	Merchant_Purchase_Struct* mps = (Merchant_Purchase_Struct*)outapp->pBuffer;

	mps->merchant.mcs_unknown001	= mp->merchant.mcs_unknown001;
	mps->merchant.merchantid		= mp->merchant.merchantid;
	mps->merchant.itemslot			= mp->merchant.itemslot;
	mps->merchant.mcs_unknown002[0] = 0x00;
	mps->merchant.mcs_unknown002[1] = 0x46;
	mps->merchant.mcs_unknown002[2] = 0x00;
	mps->merchant.quantity			= 0x00;
	mps->merchant.mcs_unknown003[0] = 0x00;
	mps->merchant.mcs_unknown003[1] = 0x00;
	mps->merchant.mcs_unknown003[2] = 0x00;
	mps->mps_unknown001				= 0x00;

	cout << "response from sell action.." << endl;
	DumpPacketHex(outapp);
	QueuePacket(outapp);
	delete outapp;

	break;
}
case OP_ShopEnd:
{
	cout << name << " is ending merchant interaction." << endl;

	APPLAYER* outapp = new APPLAYER;
	outapp->opcode = OP_ShopEndConfirm;
	outapp->pBuffer = new uchar[2];
	outapp->size = 2;
	outapp->pBuffer[0] = 0x0a;
	outapp->pBuffer[1] = 0x66;
	QueuePacket(outapp);
	delete outapp;

	break;
}
code for eq_packet_structs.h
Code:
// Darvik: shopkeeper structs
struct Merchant_Click_Struct {
	uint32 mcs_unknown001;		// unknown - first byte has different value by zone??
	uint32 merchantid;			// unique identifier for merchantID
	int8 itemslot;				// always 0x40 FROM client, 0x01 TO client. itemslot when used in purchase. 
	int8 mcs_unknown002[3];		// always 0x0b, 0x7c, 0x00 ??
	int8 quantity;				// Qty - when used in Merchant_Purchase_Struct
	int8 mcs_unknown003[3];		// always 0xa5, 0x3f ??
};

struct Merchant_Purchase_Struct {
	Merchant_Click_Struct merchant;
	int32 mps_unknown001;		// Cost in copper
};

struct Item_Shop_Struct {
	uint32 merchantid;
	int8 itemtype;
	Item_Struct item;
	int32 iss_unknown001;
};

SQL stuff:
Code:
#
# Table structure for table 'shopkeepers'
#
CREATE TABLE shopkeepers (
  id int(11) NOT NULL,
  npc_id int(11) NOT NULL,
  items_table int(11) NOT NULL,
  price_markup tinyint(2) unsigned NOT NULL default '0',
  PRIMARY KEY  (id, npc_id)
) TYPE=MyISAM;


#
# Table structure for table 'shopkeeper_items'
#
CREATE TABLE shopkeeper_items (
  id int(11) NOT NULL,
  seq tinyint(2) unsigned NOT NULL,
  item_id int(11) NOT NULL,
  qty int(11) NOT NULL,
  PRIMARY KEY (id,seq)
) TYPE=MyISAM;

#
# Dumping data for table 'shopkeepers'
#
INSERT INTO shopkeepers VALUES (1,2,1,0);

#
# Dumping data for table 'shopkeeper_items'
#
INSERT INTO shopkeeper_items VALUES (1,0,1001,99);
INSERT INTO shopkeeper_items VALUES (1,1,1002,99);
INSERT INTO shopkeeper_items VALUES (1,2,1003,99);
INSERT INTO shopkeeper_items VALUES (1,3,1004,99);
INSERT INTO shopkeeper_items VALUES (1,4,1005,99);
INSERT INTO shopkeeper_items VALUES (1,5,1006,99);
INSERT INTO shopkeeper_items VALUES (1,6,1007,99);
INSERT INTO shopkeeper_items VALUES (1,7,1008,99);
INSERT INTO shopkeeper_items VALUES (1,8,1009,99);
INSERT INTO shopkeeper_items VALUES (1,9,1010,99);
INSERT INTO shopkeeper_items VALUES (1,10,1011,99);
INSERT INTO shopkeeper_items VALUES (1,11,1012,99);
Reply With Quote
  #3  
Old 01-28-2002, 12:24 PM
DeletedUser
Fire Beetle
 
Join Date: Sep 2002
Posts: 0
Default

Good job looks great
Reply With Quote
  #4  
Old 01-28-2002, 01:29 PM
Yodason
Hill Giant
 
Join Date: Jan 2002
Posts: 205
Default

WOW! Nice job!
Reply With Quote
  #5  
Old 01-28-2002, 04:13 PM
Zeitgeist
Discordant
 
Join Date: Jan 2002
Posts: 289
Default

me getting feeling that .2.1 will be *ahem* a m*th*rf*ck*r =)
__________________
gm-Zeitgeist
I WAS Diligently Working at the Next Board Title :p
webmaster godmonkey.com, dreamusher.com
Reply With Quote
  #6  
Old 01-29-2002, 12:42 AM
Pyrotek
Sarnak
 
Join Date: Jan 2002
Posts: 97
Default

Graar.. I was working on these structs too. hehe... Good job, i'll see if we've found anything different.
Reply With Quote
  #7  
Old 01-29-2002, 01:18 AM
Drawde
Dragon
 
Join Date: Jan 2002
Posts: 521
Default

Wow, great work!
Will this code be included in the next release of the emulator?

This is just my personal opinion, but I think it might be better to have a merchant_id or similar value in npc_types, rather than a npc_type value in the merchant data. This is just from the point of view of adding merchant info to my spawn data, since my npc_type data is output by a parser program rather than written by hand, the ID of a particular NPC might change from version to version depending on what I add. Just my opinion though.

Unless you're brewing a lot of wine, however, it might be good if database merchant lists can be added so you can buy something other than grapes
Reply With Quote
  #8  
Old 01-29-2002, 03:19 AM
darvik
Fire Beetle
 
Join Date: Jan 2002
Posts: 21
Default

Yeah, I agree, the merchant_id idea was just something to avoid having to edit the npc tables and stuff .. really all you need to add into the npc_type table is probably just a merchant_table_id which refrences the list of items they sell (since multiple merchants may sell the same stuff - like a general merchant) - you can still use the table that I listed for merchant items though - I'm pretty sure I didn't miss anything required there.. although I haven't figured out what tells a merchant how many of a particular item they have.. like when a player sells something, and then you buy it back .. it's probably server-side , and there's an opcode that I missed that tells the client to delete the item from the list.. maybe I'll check into that tonight..
Reply With Quote
  #9  
Old 01-29-2002, 03:30 AM
Ukyo
Fire Beetle
 
Join Date: Jan 2002
Posts: 7
Default

From what I have seen, items are set as X for infinite amount, or # with some regen time. At least, that's how it always seemed to me.

-Ukyo
Reply With Quote
  #10  
Old 01-29-2002, 09:30 AM
DeletedUser
Fire Beetle
 
Join Date: Sep 2002
Posts: 0
Default

I will get these into the next release...
Reply With Quote
  #11  
Old 01-30-2002, 08:13 AM
Drawde
Dragon
 
Join Date: Jan 2002
Posts: 521
Default

I hope this isn't too much bother, but could you post the database table structs that you are going to use? That way I can start work on merchant data before the next emulator release.
Will you be using a merchant list with NPC IDs, or a merchant ID in the npc_types struct?
Reply With Quote
  #12  
Old 02-03-2002, 06:18 AM
Yodason
Hill Giant
 
Join Date: Jan 2002
Posts: 205
Default

I am currently working on the database code.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 09:21 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3