greggg230
06-12-2008, 06:27 PM
As of right now, the displayed price for an item (whether selling or buying) does not match up to the actual price you see. For example, when you sell an item, you get more money than what is displayed. In addition, everyone, regardless of faction or charisma, receives the same prices.
I looked through the code and it was really a mess. There is an existing function in client.cpp which determines a modifier based on charisma and function, but it isn't used anywhere. Further, the code contains no less than three different, contradictory rates for merchants.
So, I polished up the code and fixed the relevant functions, they're down below. If you want to just download the updated .cpp files, here they are: http://70.189.190.124:8000/merchantfix.rar
Client::BulkSendMerchantInventory in zone/client_process.cpp:
void Client::BulkSendMerchantInventory(int merchant_id, int16 npcid) {
const Item_Struct* handyitem = NULL;
int32 numItemSlots=80; //The max number of items passed in the transaction.
const Item_Struct *item;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr;
Mob* merch = entity_list.GetMobByNpcTypeID(npcid);
if(merlist.size()==0){ //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded
zone->LoadNewMerchantData(merchant_id);
merlist = zone->merchanttable[merchant_id];
if(merlist.size()==0)
return;
}
std::list<TempMerchantList> tmp_merlist = zone->tmpmerchanttable[npcid];
std::list<TempMerchantList>::iterator tmp_itr;
int32 i=1;
int8 handychance = 0;
for(itr = merlist.begin();itr != merlist.end() && i<numItemSlots;itr++){
MerchantList ml = *itr;
handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 );
item = database.GetItem(ml.item);
if(item) {
if(handychance==0)
handyitem=item;
else
handychance--;
int charges=1;
if(item->ItemClass==ItemClassCommon)
charges=item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
inst->SetPrice((item->Price*(1/.884)*Client::CalcPriceMod(merch,false)));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(-1); //unlimited
if(charges > 0)
inst->SetCharges(charges);
else
inst->SetCharges(1);
SendItemPacket(ml.slot-1, inst, ItemPacketMerchant);
safe_delete(inst);
}
}
i++;
}
std::list<TempMerchantList> origtmp_merlist = zone->tmpmerchanttable[npcid];
tmp_merlist.clear();
for(tmp_itr = origtmp_merlist.begin();tmp_itr != origtmp_merlist.end() && i<numItemSlots;tmp_itr++){
TempMerchantList ml = *tmp_itr;
item=database.GetItem(ml.item);
ml.slot=i;
if (item) {
if(handychance==0)
handyitem=item;
else
handychance--;
int charges=1;
if(item->ItemClass==ItemClassCommon && (sint16)ml.charges <= item->MaxCharges)
charges=ml.charges;
else
charges = item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
inst->SetPrice((item->Price*(1/.884)*Client::CalcPriceMod(merch,false)));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(1);
if(charges > 0)
inst->SetCharges(charges);
else
inst->SetCharges(1);
SendItemPacket(ml.slot-1, inst, ItemPacketMerchant);
safe_delete(inst);
}
}
tmp_merlist.push_back(ml);
i++;
}
//this resets the slot
zone->tmpmerchanttable[npcid] = tmp_merlist;
if(merch != NULL && handyitem){
char handy_id[8]={0};
int greeting=rand()%5;
int greet_id=0;
switch(greeting){
case 1:
greet_id=MERCHANT_GREETING;
break;
case 2:
greet_id=MERCHANT_HANDY_ITEM1;
break;
case 3:
greet_id=MERCHANT_HANDY_ITEM2;
break;
case 4:
greet_id=MERCHANT_HANDY_ITEM3;
break;
default:
greet_id=MERCHANT_HANDY_ITEM4;
}
sprintf(handy_id,"%i",greet_id);
if(greet_id!=MERCHANT_GREETING)
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName(),handyitem->Name);
else
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName());
merch->CastToNPC()->FaceTarget(this->CastToMob());
}
// safe_delete_array(cpi);
}
Client::Handle_OP_ShopRequest in zone/client_packet.cpp
void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
{
// this works
Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer;
if (app->size != sizeof(Merchant_Click_Struct))
return;
// Send back opcode OP_ShopRequest - tells client to open merchant window.
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
//Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
int merchantid=0;
Mob* tmp = entity_list.GetMob(mc->npcid);
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT)
return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*tmp) > USE_NPC_RANGE2)
return;
merchantid=tmp->CastToNPC()->MerchantType;
int action = 1;
if(merchantid == 0) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = 1; //open...
mco->rate = 1.0;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
if(tmp->IsEngaged()){
this->Message_StringID(0,MERCHANT_BUSY);
action = 0;
}
if (GetFeigned() || IsInvisible())
{
Message(0,"You cannot use a merchant right now.");
action = 0;
}
int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), tmp->CastToNPC()->GetPrimaryFaction(), tmp);
if(factionlvl >= 6 && factionlvl != 9)
{
Message(0,"I will not deal with one such as you!");
action = 0;
}
if (tmp->Charmed())
{
action = 0;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = action; // Merchant command 0x01 = open
mco->rate = 1/(.884*Client::CalcPriceMod(tmp,true)); // works
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
if (action == 1)
BulkSendMerchantInventory(merchantid,tmp->GetNPCTypeID());
return;
}
Client::Handle_OP_ShopPlayerBuy in zone/client_packet.cpp
void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
{
RDTSC_Timer t1;
t1.start();
Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer;
#if EQDEBUG >= 5
LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName());
DumpPacket(app);
#endif
int merchantid;
bool tmpmer_used = false;
Mob* tmp = entity_list.GetMob(mp->npcid);
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT)
return;
if (mp->quantity < 1) return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*tmp) > USE_NPC_RANGE2)
return;
merchantid=tmp->CastToNPC()->MerchantType;
uint32 item_id = 0;
std::list<MerchantList> merlist = zone->merchanttable[merchantid];
std::list<MerchantList>::const_iterator itr;
for(itr = merlist.begin();itr != merlist.end();itr++){
MerchantList ml = *itr;
if(mp->itemslot == ml.slot){
item_id = ml.item;
break;
}
}
const Item_Struct* item = NULL;
int32 prevcharges = 0;
if (item_id == 0) { //check to see if its on the temporary table
std::list<TempMerchantList> tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()];
std::list<TempMerchantList>::const_iterator tmp_itr;
TempMerchantList ml;
for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();tmp_itr++){
ml = *tmp_itr;
if(mp->itemslot == ml.slot){
item_id = ml.item;
tmpmer_used = true;
prevcharges = ml.charges;
break;
}
}
}
item = database.GetItem(item_id);
if (!item){
//error finding item, client didnt get the update packet for whatever reason, roleplay a tad
Message(15,"%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.",tmp->GetCleanName());
EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct));
Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer;
delitem->itemslot = mp->itemslot;
delitem->npcid = mp->npcid;
delitem->playerid = mp->playerid;
delitempacket->priority = 6;
entity_list.QueueCloseClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update
safe_delete(delitempacket);
return;
}
if (CheckLoreConflict(item))
{
Message(15,"You can only have one of a lore item.");
return;
}
if(tmpmer_used && (mp->quantity > prevcharges))
mp->quantity = prevcharges;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer;
mpo->quantity = mp->quantity;
mpo->playerid = mp->playerid;
mpo->npcid = mp->npcid;
mpo->itemslot=mp->itemslot;
sint16 freeslotid=0;
ItemInst* inst = database.CreateItem(item, mp->quantity);
bool stacked = TryStacking(inst);
if(!stacked)
freeslotid = m_inv.FindFreeSlot(false, true, item->Size);
//make sure we are not completely full...
if(freeslotid == SLOT_CURSOR) {
if(m_inv.GetItem(SLOT_CURSOR) != NULL) {
Message(13, "You do not have room for any more items.");
safe_delete(outapp);
safe_delete(inst);
return;
}
}
mpo->price = (item->Price*(1/.884)*Client::CalcPriceMod(tmp,false))*mp->quantity;
if(freeslotid == SLOT_INVALID || !TakeMoneyFromPP(mpo->price))
{
safe_delete(outapp);
safe_delete(inst);
return;
}
string packet;
if(mp->quantity==1 && item->MaxCharges>0 && item->MaxCharges<255)
mp->quantity=item->MaxCharges;
if (!stacked && inst) {
PutItemInInventory(freeslotid, *inst);
SendItemPacket(freeslotid, inst, ItemPacketTrade);
}
else if(!stacked){
LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass);
}
QueuePacket(outapp);
if(inst && tmpmer_used){
sint32 new_charges = prevcharges - mp->quantity;
zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(),item_id,new_charges);
if(new_charges<=0){
EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct));
Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer;
delitem->itemslot = mp->itemslot;
delitem->npcid = mp->npcid;
delitem->playerid = mp->playerid;
delitempacket->priority = 6;
entity_list.QueueClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update
safe_delete(delitempacket);
}
}
safe_delete(inst);
safe_delete(outapp);
if (zone->merchantvar!=0){
if (zone->merchantvar==7){
LogMerchant(this,tmp,mpo,item,true);
}
else if ((admin>=10) && (admin<20)){
if ((zone->merchantvar<8) && (zone->merchantvar>5))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=20){
if ((zone->merchantvar<8) && (zone->merchantvar>4))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=80){
if ((zone->merchantvar<8) && (zone->merchantvar>3))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=100){
if ((zone->merchantvar<9) && (zone->merchantvar>2))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=150){
if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=255){
if ((zone->merchantvar<8) && (zone->merchantvar>0))
LogMerchant(this,tmp,mpo,item,true);
}
}
t1.stop();
cout << "At 1: " << t1.getDuration() << endl;
return;
}
Client::Handle_OP_ShopPlayerSell in zone/client_packet.cpp
void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
{
RDTSC_Timer t1(true);
Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer;
Mob* vendor = entity_list.GetMob(mp->npcid);
if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT)
return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*vendor) > USE_NPC_RANGE2)
return;
int32 price=0;
int32 itemid = GetItemIDAt(mp->itemslot);
if(itemid == 0)
return;
const Item_Struct* item = database.GetItem(itemid);
ItemInst* inst = GetInv().GetItem(mp->itemslot);
if(!inst){
Message(13,"You seemed to have misplaced that item..");
return;
}
if(mp->quantity > 1 && (sint16)mp->quantity > inst->GetCharges())
return;
if (item && !item->NoDrop) {
return;
}
if (item){
price=(int)((item->Price*mp->quantity)*.884*Client::CalcPriceMod(vendor,true)+0 .5); // need to round up, because client does it automatically when displaying price
AddMoneyToPP(price,false);
if (zone->merchantvar!=0){
if (zone->merchantvar==7) {
LogMerchant(this,vendor,mp,item,false);
}
else if ((admin>=10) && (admin<20)) {
if ((zone->merchantvar<8) && (zone->merchantvar>5))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=20) {
if ((zone->merchantvar<8) && (zone->merchantvar>4))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=80) {
if ((zone->merchantvar<8) && (zone->merchantvar>3))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=100) {
if ((zone->merchantvar<9) && (zone->merchantvar>2))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=150) {
if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=255) {
if ((zone->merchantvar<8) && (zone->merchantvar>0))
LogMerchant(this,vendor,mp,item,false);
}
}
}
else
Message(0, "Error #1, item == 0");
if (item && inst->IsStackable())
{
unsigned int i_quan = inst->GetCharges();
if (mp->quantity > i_quan)
mp->quantity = i_quan;
}
else
{
mp->quantity = 1;
}
int freeslot = 0;
int charges = 0;
if(inst->IsStackable())
charges = mp->quantity;
else
charges = inst->GetCharges();
if((freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){
ItemInst* inst2 = inst->Clone();
inst2->SetPrice(item->Price*(1/.884)*Client::CalcPriceMod(vendor,false));
inst2->SetMerchantSlot(freeslot);
if(inst2->IsStackable())
inst2->SetCharges(mp->quantity);
SendItemPacket(freeslot-1, inst2, ItemPacketMerchant);
safe_delete(inst2);
}
// Now remove the item from the player, this happens irrguardless of outcome
if (!inst->IsStackable())
this->DeleteItemInInventory(mp->itemslot,0,false);
else
this->DeleteItemInInventory(mp->itemslot,mp->quantity,false);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer;
mco->npcid = vendor->GetID();
mco->itemslot=mp->itemslot;
mco->quantity=mp->quantity;
mco->price=price;
QueuePacket(outapp);
safe_delete(outapp);
t1.start();
Save(1);
t1.stop();
cout << "Save took: " << t1.getDuration() << endl;
return;
}
Also, should this make the official release, there is a comment in Live_structs.h which says the "rate" variable in Merchant_Click_Struct no longer works. This is no longer true, so it should be taken out.
I looked through the code and it was really a mess. There is an existing function in client.cpp which determines a modifier based on charisma and function, but it isn't used anywhere. Further, the code contains no less than three different, contradictory rates for merchants.
So, I polished up the code and fixed the relevant functions, they're down below. If you want to just download the updated .cpp files, here they are: http://70.189.190.124:8000/merchantfix.rar
Client::BulkSendMerchantInventory in zone/client_process.cpp:
void Client::BulkSendMerchantInventory(int merchant_id, int16 npcid) {
const Item_Struct* handyitem = NULL;
int32 numItemSlots=80; //The max number of items passed in the transaction.
const Item_Struct *item;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr;
Mob* merch = entity_list.GetMobByNpcTypeID(npcid);
if(merlist.size()==0){ //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded
zone->LoadNewMerchantData(merchant_id);
merlist = zone->merchanttable[merchant_id];
if(merlist.size()==0)
return;
}
std::list<TempMerchantList> tmp_merlist = zone->tmpmerchanttable[npcid];
std::list<TempMerchantList>::iterator tmp_itr;
int32 i=1;
int8 handychance = 0;
for(itr = merlist.begin();itr != merlist.end() && i<numItemSlots;itr++){
MerchantList ml = *itr;
handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 );
item = database.GetItem(ml.item);
if(item) {
if(handychance==0)
handyitem=item;
else
handychance--;
int charges=1;
if(item->ItemClass==ItemClassCommon)
charges=item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
inst->SetPrice((item->Price*(1/.884)*Client::CalcPriceMod(merch,false)));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(-1); //unlimited
if(charges > 0)
inst->SetCharges(charges);
else
inst->SetCharges(1);
SendItemPacket(ml.slot-1, inst, ItemPacketMerchant);
safe_delete(inst);
}
}
i++;
}
std::list<TempMerchantList> origtmp_merlist = zone->tmpmerchanttable[npcid];
tmp_merlist.clear();
for(tmp_itr = origtmp_merlist.begin();tmp_itr != origtmp_merlist.end() && i<numItemSlots;tmp_itr++){
TempMerchantList ml = *tmp_itr;
item=database.GetItem(ml.item);
ml.slot=i;
if (item) {
if(handychance==0)
handyitem=item;
else
handychance--;
int charges=1;
if(item->ItemClass==ItemClassCommon && (sint16)ml.charges <= item->MaxCharges)
charges=ml.charges;
else
charges = item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
inst->SetPrice((item->Price*(1/.884)*Client::CalcPriceMod(merch,false)));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(1);
if(charges > 0)
inst->SetCharges(charges);
else
inst->SetCharges(1);
SendItemPacket(ml.slot-1, inst, ItemPacketMerchant);
safe_delete(inst);
}
}
tmp_merlist.push_back(ml);
i++;
}
//this resets the slot
zone->tmpmerchanttable[npcid] = tmp_merlist;
if(merch != NULL && handyitem){
char handy_id[8]={0};
int greeting=rand()%5;
int greet_id=0;
switch(greeting){
case 1:
greet_id=MERCHANT_GREETING;
break;
case 2:
greet_id=MERCHANT_HANDY_ITEM1;
break;
case 3:
greet_id=MERCHANT_HANDY_ITEM2;
break;
case 4:
greet_id=MERCHANT_HANDY_ITEM3;
break;
default:
greet_id=MERCHANT_HANDY_ITEM4;
}
sprintf(handy_id,"%i",greet_id);
if(greet_id!=MERCHANT_GREETING)
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName(),handyitem->Name);
else
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName());
merch->CastToNPC()->FaceTarget(this->CastToMob());
}
// safe_delete_array(cpi);
}
Client::Handle_OP_ShopRequest in zone/client_packet.cpp
void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
{
// this works
Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer;
if (app->size != sizeof(Merchant_Click_Struct))
return;
// Send back opcode OP_ShopRequest - tells client to open merchant window.
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
//Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
int merchantid=0;
Mob* tmp = entity_list.GetMob(mc->npcid);
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT)
return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*tmp) > USE_NPC_RANGE2)
return;
merchantid=tmp->CastToNPC()->MerchantType;
int action = 1;
if(merchantid == 0) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = 1; //open...
mco->rate = 1.0;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
if(tmp->IsEngaged()){
this->Message_StringID(0,MERCHANT_BUSY);
action = 0;
}
if (GetFeigned() || IsInvisible())
{
Message(0,"You cannot use a merchant right now.");
action = 0;
}
int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), tmp->CastToNPC()->GetPrimaryFaction(), tmp);
if(factionlvl >= 6 && factionlvl != 9)
{
Message(0,"I will not deal with one such as you!");
action = 0;
}
if (tmp->Charmed())
{
action = 0;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = action; // Merchant command 0x01 = open
mco->rate = 1/(.884*Client::CalcPriceMod(tmp,true)); // works
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
if (action == 1)
BulkSendMerchantInventory(merchantid,tmp->GetNPCTypeID());
return;
}
Client::Handle_OP_ShopPlayerBuy in zone/client_packet.cpp
void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
{
RDTSC_Timer t1;
t1.start();
Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer;
#if EQDEBUG >= 5
LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName());
DumpPacket(app);
#endif
int merchantid;
bool tmpmer_used = false;
Mob* tmp = entity_list.GetMob(mp->npcid);
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT)
return;
if (mp->quantity < 1) return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*tmp) > USE_NPC_RANGE2)
return;
merchantid=tmp->CastToNPC()->MerchantType;
uint32 item_id = 0;
std::list<MerchantList> merlist = zone->merchanttable[merchantid];
std::list<MerchantList>::const_iterator itr;
for(itr = merlist.begin();itr != merlist.end();itr++){
MerchantList ml = *itr;
if(mp->itemslot == ml.slot){
item_id = ml.item;
break;
}
}
const Item_Struct* item = NULL;
int32 prevcharges = 0;
if (item_id == 0) { //check to see if its on the temporary table
std::list<TempMerchantList> tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()];
std::list<TempMerchantList>::const_iterator tmp_itr;
TempMerchantList ml;
for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();tmp_itr++){
ml = *tmp_itr;
if(mp->itemslot == ml.slot){
item_id = ml.item;
tmpmer_used = true;
prevcharges = ml.charges;
break;
}
}
}
item = database.GetItem(item_id);
if (!item){
//error finding item, client didnt get the update packet for whatever reason, roleplay a tad
Message(15,"%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.",tmp->GetCleanName());
EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct));
Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer;
delitem->itemslot = mp->itemslot;
delitem->npcid = mp->npcid;
delitem->playerid = mp->playerid;
delitempacket->priority = 6;
entity_list.QueueCloseClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update
safe_delete(delitempacket);
return;
}
if (CheckLoreConflict(item))
{
Message(15,"You can only have one of a lore item.");
return;
}
if(tmpmer_used && (mp->quantity > prevcharges))
mp->quantity = prevcharges;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer;
mpo->quantity = mp->quantity;
mpo->playerid = mp->playerid;
mpo->npcid = mp->npcid;
mpo->itemslot=mp->itemslot;
sint16 freeslotid=0;
ItemInst* inst = database.CreateItem(item, mp->quantity);
bool stacked = TryStacking(inst);
if(!stacked)
freeslotid = m_inv.FindFreeSlot(false, true, item->Size);
//make sure we are not completely full...
if(freeslotid == SLOT_CURSOR) {
if(m_inv.GetItem(SLOT_CURSOR) != NULL) {
Message(13, "You do not have room for any more items.");
safe_delete(outapp);
safe_delete(inst);
return;
}
}
mpo->price = (item->Price*(1/.884)*Client::CalcPriceMod(tmp,false))*mp->quantity;
if(freeslotid == SLOT_INVALID || !TakeMoneyFromPP(mpo->price))
{
safe_delete(outapp);
safe_delete(inst);
return;
}
string packet;
if(mp->quantity==1 && item->MaxCharges>0 && item->MaxCharges<255)
mp->quantity=item->MaxCharges;
if (!stacked && inst) {
PutItemInInventory(freeslotid, *inst);
SendItemPacket(freeslotid, inst, ItemPacketTrade);
}
else if(!stacked){
LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass);
}
QueuePacket(outapp);
if(inst && tmpmer_used){
sint32 new_charges = prevcharges - mp->quantity;
zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(),item_id,new_charges);
if(new_charges<=0){
EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct));
Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer;
delitem->itemslot = mp->itemslot;
delitem->npcid = mp->npcid;
delitem->playerid = mp->playerid;
delitempacket->priority = 6;
entity_list.QueueClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update
safe_delete(delitempacket);
}
}
safe_delete(inst);
safe_delete(outapp);
if (zone->merchantvar!=0){
if (zone->merchantvar==7){
LogMerchant(this,tmp,mpo,item,true);
}
else if ((admin>=10) && (admin<20)){
if ((zone->merchantvar<8) && (zone->merchantvar>5))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=20){
if ((zone->merchantvar<8) && (zone->merchantvar>4))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=80){
if ((zone->merchantvar<8) && (zone->merchantvar>3))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=100){
if ((zone->merchantvar<9) && (zone->merchantvar>2))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=150){
if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
LogMerchant(this,tmp,mpo,item,true);
}
else if (admin<=255){
if ((zone->merchantvar<8) && (zone->merchantvar>0))
LogMerchant(this,tmp,mpo,item,true);
}
}
t1.stop();
cout << "At 1: " << t1.getDuration() << endl;
return;
}
Client::Handle_OP_ShopPlayerSell in zone/client_packet.cpp
void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
{
RDTSC_Timer t1(true);
Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer;
Mob* vendor = entity_list.GetMob(mp->npcid);
if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT)
return;
//you have to be somewhat close to them to be properly using them
if(DistNoRoot(*vendor) > USE_NPC_RANGE2)
return;
int32 price=0;
int32 itemid = GetItemIDAt(mp->itemslot);
if(itemid == 0)
return;
const Item_Struct* item = database.GetItem(itemid);
ItemInst* inst = GetInv().GetItem(mp->itemslot);
if(!inst){
Message(13,"You seemed to have misplaced that item..");
return;
}
if(mp->quantity > 1 && (sint16)mp->quantity > inst->GetCharges())
return;
if (item && !item->NoDrop) {
return;
}
if (item){
price=(int)((item->Price*mp->quantity)*.884*Client::CalcPriceMod(vendor,true)+0 .5); // need to round up, because client does it automatically when displaying price
AddMoneyToPP(price,false);
if (zone->merchantvar!=0){
if (zone->merchantvar==7) {
LogMerchant(this,vendor,mp,item,false);
}
else if ((admin>=10) && (admin<20)) {
if ((zone->merchantvar<8) && (zone->merchantvar>5))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=20) {
if ((zone->merchantvar<8) && (zone->merchantvar>4))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=80) {
if ((zone->merchantvar<8) && (zone->merchantvar>3))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=100) {
if ((zone->merchantvar<9) && (zone->merchantvar>2))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=150) {
if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
LogMerchant(this,vendor,mp,item,false);
}
else if (admin<=255) {
if ((zone->merchantvar<8) && (zone->merchantvar>0))
LogMerchant(this,vendor,mp,item,false);
}
}
}
else
Message(0, "Error #1, item == 0");
if (item && inst->IsStackable())
{
unsigned int i_quan = inst->GetCharges();
if (mp->quantity > i_quan)
mp->quantity = i_quan;
}
else
{
mp->quantity = 1;
}
int freeslot = 0;
int charges = 0;
if(inst->IsStackable())
charges = mp->quantity;
else
charges = inst->GetCharges();
if((freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){
ItemInst* inst2 = inst->Clone();
inst2->SetPrice(item->Price*(1/.884)*Client::CalcPriceMod(vendor,false));
inst2->SetMerchantSlot(freeslot);
if(inst2->IsStackable())
inst2->SetCharges(mp->quantity);
SendItemPacket(freeslot-1, inst2, ItemPacketMerchant);
safe_delete(inst2);
}
// Now remove the item from the player, this happens irrguardless of outcome
if (!inst->IsStackable())
this->DeleteItemInInventory(mp->itemslot,0,false);
else
this->DeleteItemInInventory(mp->itemslot,mp->quantity,false);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer;
mco->npcid = vendor->GetID();
mco->itemslot=mp->itemslot;
mco->quantity=mp->quantity;
mco->price=price;
QueuePacket(outapp);
safe_delete(outapp);
t1.start();
Save(1);
t1.stop();
cout << "Save took: " << t1.getDuration() << endl;
return;
}
Also, should this make the official release, there is a comment in Live_structs.h which says the "rate" variable in Merchant_Click_Struct no longer works. This is no longer true, so it should be taken out.