(RoF2) Bazaar Trading (Buying/Selling) is now fully functional. Bazaar (/bazaar) search is not yet functional.

This commit is contained in:
Trevius 2015-02-16 11:56:23 -06:00
parent db3feafe48
commit 28be3b87b7
9 changed files with 375 additions and 157 deletions

View File

@ -1,5 +1,8 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 02/16/2015 ==
Trevius: (RoF2) Bazaar Trading (Buying/Selling) is now fully functional. Bazaar (/bazaar) search is not yet functional.
== 02/14/2015 ==
Trevius: (RoF2) Bazaar is now partially functional. RoF2 clients can start/end trader mode and other clients can purchase from them. No other functionality yet.

View File

@ -3578,14 +3578,13 @@ namespace RoF2
// Live actually has 200 items now, but 80 is the most our internal struct supports
for (uint32 i = 0; i < 200; i++)
{
//strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber));
//snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]);
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", 0);
eq->items[i].Unknown18 = 0;
if (i < 80) {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]);
eq->ItemCost[i] = emu->ItemCost[i];
}
else {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", 0);
eq->ItemCost[i] = 0;
}
}
@ -3637,18 +3636,61 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_TraderShop)
ENCODE(OP_TraderDelItem)
{
ENCODE_LENGTH_EXACT(TraderClick_Struct);
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
ENCODE_LENGTH_EXACT(TraderDelItem_Struct);
SETUP_DIRECT_ENCODE(TraderDelItem_Struct, structs::TraderDelItem_Struct);
//eq->Code = emu->Unknown004;
OUT(TraderID);
OUT(Approval);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderDelItem): TraderID %d, SerialNumber: %d", emu->TraderID, emu->ItemID);
FINISH_ENCODE();
}
ENCODE(OP_TraderShop)
{
uint32 psize = (*p)->size;
if (psize == sizeof(TraderClick_Struct))
{
ENCODE_LENGTH_EXACT(TraderClick_Struct);
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
eq->Code = 28; // Seen on Live
OUT(TraderID);
OUT(Approval);
FINISH_ENCODE();
}
else if (psize == sizeof(TraderBuy_Struct))
{
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
OUT(Action);
OUT(TraderID);
//memcpy(eq->BuyerName, emu->BuyerName, sizeof(eq->BuyerName));
//memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
OUT(ItemID);
OUT(AlreadySold);
OUT(Price);
OUT(Quantity);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, emu->ItemName);
FINISH_ENCODE();
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Encode Size Unknown (%d)", psize);
}
}
ENCODE(OP_TributeInfo)
{
ENCODE_LENGTH_ATLEAST(TributeAbility_Struct);
@ -4961,19 +5003,38 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
//MEMSET_IN(TraderClick_Struct);
//emu->Unknown004 = eq->Code;
IN(TraderID);
IN(Approval);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Decoded packet size (%d) to size (%d)", sizeof(structs::TraderClick_Struct), sizeof(TraderClick_Struct));
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::TraderBuy_Struct))
{
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
IN(Action);
IN(Price);
IN(TraderID);
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
IN(ItemID);
IN(Quantity);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): (Unknowns) Unknown004 %d, Unknown008 %d, Unknown012 %d, Unknown076 %d, Unknown276 %d",
eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName);
FINISH_DIRECT_DECODE();
}
else if (psize == 4)
{
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Forwarding packet as-is with size 4");
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Decode Size Mismatch (%d), expected (%d)", psize, sizeof(structs::TraderClick_Struct));
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Decode Size Unknown (%d)", psize);
}
}

View File

@ -3,6 +3,7 @@
E(OP_SendMembershipDetails)
E(OP_TraderShop)
E(OP_TraderDelItem)
// incoming packets that require a DECODE translation:
// Begin RoF2 Decodes

View File

@ -3224,6 +3224,26 @@ struct TraderStatus_Struct {
};
struct TraderBuy_Struct {
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Unknown008;
/*012*/ uint32 Unknown012;
/*016*/ uint32 TraderID;
/*020*/ char BuyerName[64];
/*084*/ char SellerName[64];
/*148*/ char Unknown148[32];
/*180*/ char ItemName[64];
/*244*/ char SerialNumber[16];
/*260*/ uint32 Unknown076;
/*264*/ uint32 ItemID;
/*268*/ uint32 Price;
/*272*/ uint32 AlreadySold;
/*276*/ uint32 Unknown276;
/*280*/ uint32 Quantity;
/*284*/
};
struct TraderBuy_Struct_OLD {
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Price;
@ -3253,19 +3273,12 @@ struct MoneyUpdate_Struct{
int32 copper;
};
//struct MoneyUpdate_Struct
//{
//*0000*/ uint32 spawn_id; // ***Placeholder
//*0004*/ uint32 cointype; // Coin Type
//*0008*/ uint32 amount; // Amount
//*0012*/
//};
struct TraderDelItem_Struct{
uint32 slotid;
uint32 quantity;
uint32 unknown;
/*000*/ uint32 Unknown000;
/*004*/ uint32 TraderID;
/*008*/ char SerialNumber[16];
/*024*/ uint32 Unknown012;
/*028*/
};
struct TraderClick_Struct{

View File

@ -402,12 +402,12 @@ OP_LootComplete=0x55c4
# bazaar trader stuff:
OP_BazaarSearch=0x39d6
OP_TraderDelItem=0x0000
OP_TraderDelItem=0x5829
OP_BecomeTrader=0x61b3
OP_TraderShop=0x31df
OP_Trader=0x4ef5
OP_TraderBuy=0x0000
OP_Barter=0x243a
OP_TraderBuy=0x0000
OP_ShopItem=0x0000
OP_BazaarInspect=0x0000
OP_Bazaar=0x0000

View File

@ -165,6 +165,7 @@ Client::Client(EQStreamInterface* ieqs)
Trader=false;
Buyer = false;
CustomerID = 0;
TraderID = 0;
TrackingID = 0;
WID = 0;
account_id = 0;

View File

@ -266,10 +266,11 @@ public:
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
void SendTraderItem(uint32 item_id,uint16 quantity);
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
uint32 FindTraderItemSerialNumber(int32 ItemID);
ItemInst* FindTraderItemBySerialNumber(int32 SerialNumber);
void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot);
void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges);
void NukeTraderItem(uint16 slot, int16 charges, uint16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);
void TradeRequestFailed(const EQApplicationPacket* app);
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
void TraderUpdate(uint16 slot_id,uint32 trader_id);
@ -1388,6 +1389,7 @@ private:
uint16 BoatID;
uint16 TrackingID;
uint16 CustomerID;
uint16 TraderID;
uint32 account_creation;
uint8 firstlogon;
uint32 mercid; // current merc

View File

@ -11952,12 +11952,6 @@ void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app)
{
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2);
//outapp->pBuffer[0] = 0x0a;
//outapp->pBuffer[1] = 0x66;
//QueuePacket(outapp);
//safe_delete(outapp);
//Save();
return;
}
@ -13443,23 +13437,22 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
//
// Client has elected to buy an item from a Trader
//
if (app->size != sizeof(TraderBuy_Struct)) {
Log.Out(Logs::General, Logs::Error, "Wrong size: OP_TraderBuy, size=%i, expected %i", app->size, sizeof(TraderBuy_Struct));
return;
}
if (app->size == sizeof(TraderBuy_Struct)){
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Buy Trader Item ");
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Buy Trader Item ");
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Struct size mismatch");
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
return;
}
@ -13521,56 +13514,105 @@ void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app)
void Client::Handle_OP_TraderShop(const EQApplicationPacket *app)
{
// Bazaar Trader:
//
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
//
if (app->size != sizeof(TraderClick_Struct)) {
Log.Out(Logs::General, Logs::Error, "Wrong size: OP_TraderShop, size=%i, expected %i", app->size, sizeof(TraderClick_Struct));
return;
}
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Trader = entity_list.GetClientByID(tcs->TraderID);
if (Trader)
if (app->size == sizeof(TraderClick_Struct))
{
outtcs->Approval = Trader->WithCustomer(GetID());
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Trader = entity_list.GetClientByID(tcs->TraderID);
if (Trader)
{
outtcs->Approval = Trader->WithCustomer(GetID());
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
return;
}
outtcs->TraderID = tcs->TraderID;
outtcs->Unknown008 = 0x3f800000;
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Trader->CharacterID());
Trader->Trader_CustomerBrowsing(this);
TraderID = tcs->TraderID;
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent");
}
else
{
Message_StringID(clientMessageYellow, TRADER_BUSY);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy");
}
safe_delete(outapp);
return;
}
else if (app->size == sizeof(TraderBuy_Struct))
{
// RoF+
// Customer has purchased an item from the Trader
outtcs->TraderID = tcs->TraderID;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
outtcs->Unknown008 = 0x3f800000;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID))
{
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
tbs->Action, tbs->Price, tbs->TraderID, tbs->ItemID, tbs->Quantity, tbs->ItemName);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "OP_TraderShop: Null Client Pointer");
}
}
else if (app->size == 4)
{
// RoF+
// Customer has closed the trade window
uint32 Command = *((uint32 *)app->pBuffer);
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Trader->CharacterID());
Trader->Trader_CustomerBrowsing(this);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent");
if (Command == 4)
{
Client* c = entity_list.GetClientByID(TraderID);
TraderID = 0;
if (c)
{
c->WithCustomer(0);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction - Code %d", Command);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer for Trader - Code %d", Command);
}
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unhandled Code %d", Command);
}
}
else
{
Message_StringID(clientMessageYellow, TRADER_BUSY);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy");
Log.Out(Logs::Detail, Logs::Trading, "Unknown size for OP_TraderShop: %i\n", app->size);
Log.Out(Logs::General, Logs::Error, "Unknown size for OP_TraderShop: %i\n", app->size);
DumpPacket(app);
return;
}
safe_delete(outapp);
return;
}
void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app)

View File

@ -1098,7 +1098,15 @@ void Client::Trader_EndTrader() {
for(int i = 0; i < 80; i++) {
if(gis->Items[i] != 0) {
tdis->ItemID = gis->SerialNumber[i];
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = gis->Items[i];
}
else
{
tdis->ItemID = gis->SerialNumber[i];
}
Customer->QueuePacket(outapp);
}
@ -1220,6 +1228,29 @@ void Client::BulkSendTraderInventory(uint32 char_id) {
safe_delete(TraderItems);
}
uint32 Client::FindTraderItemSerialNumber(int32 ItemID) {
ItemInst* item = nullptr;
uint16 SlotID = 0;
for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; i++){
item = this->GetInv().GetItem(i);
if (item && item->GetItem()->ID == 17899){ //Traders Satchel
for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; x++) {
// we already have the parent bag and a contents iterator..why not just iterate the bag!??
SlotID = Inventory::CalcSlotId(i, x);
item = this->GetInv().GetItem(SlotID);
if (item) {
if (item->GetID() == ItemID)
return item->GetSerialNumber();
}
}
}
}
Log.Out(Logs::Detail, Logs::Trading, "Client::FindTraderItemSerialNumber Couldn't find item! Item ID %i", ItemID);
return 0;
}
ItemInst* Client::FindTraderItemBySerialNumber(int32 SerialNumber){
ItemInst* item = nullptr;
@ -1287,9 +1318,9 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){
item = this->GetInv().GetItem(SlotID);
if(item && item->GetSerialNumber() == SerialNumber &&
(item->GetCharges() >= Quantity || (item->GetCharges() <= 0 && Quantity == 1))){
if (item && item->GetSerialNumber() == SerialNumber &&
(item->GetCharges() >= Quantity || (item->GetCharges() <= 0 && Quantity == 1)))
{
return SlotID;
}
}
@ -1301,21 +1332,34 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){
return 0;
}
void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int SerialNumber) {
void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int32 SerialNumber, int32 itemid) {
if(!Customer)
return;
if(!Customer) return;
Log.Out(Logs::Detail, Logs::Trading, "NukeTraderItem(Slot %i, Charges %i, Quantity %i", Slot, Charges, Quantity);
if(Quantity < Charges) {
if(Quantity < Charges)
{
Customer->SendSingleTraderItem(this->CharacterID(), SerialNumber);
m_inv.DeleteItem(Slot, Quantity);
}
else {
else
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderDelItem,sizeof(TraderDelItem_Struct));
TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer;
tdis->Unknown000 = 0;
tdis->TraderID = Customer->GetID();
tdis->ItemID = SerialNumber;
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = itemid;
}
else
{
tdis->ItemID = SerialNumber;
}
tdis->Unknown012 = 0;
@ -1323,7 +1367,6 @@ void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Cu
safe_delete(outapp);
m_inv.DeleteItem(Slot);
}
// This updates the trader. Removes it from his trading bags.
//
@ -1374,49 +1417,61 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client*
int16 Charges=0;
uint16 SlotID = FindTraderItem(SerialNumber, Quantity);
if(SlotID > 0){
if(SlotID > 0) {
item = this->GetInv().GetItem(SlotID);
if(item) {
Charges = this->GetInv().GetItem(SlotID)->GetCharges();
Stackable = item->IsStackable();
if(!Stackable)
Quantity = (Charges > 0) ? Charges : 1;
Log.Out(Logs::Detail, Logs::Trading, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity);
if (!item)
{
Log.Out(Logs::Detail, Logs::Trading, "Could not find Item: %i on Trader: %s", SerialNumber, Quantity, this->GetName());
return;
}
if(item && (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable)){
Charges = this->GetInv().GetItem(SlotID)->GetCharges();
Stackable = item->IsStackable();
if (!Stackable)
Quantity = (Charges > 0) ? Charges : 1;
Log.Out(Logs::Detail, Logs::Trading, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity);
if (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable)
{
this->DeleteItemInInventory(SlotID, Quantity);
TraderCharges_Struct* GetSlot = database.LoadTraderItemWithCharges(this->CharacterID());
TraderCharges_Struct* TraderItems = database.LoadTraderItemWithCharges(this->CharacterID());
uint8 Count = 0;
bool TestSlot = true;
for(int y = 0;y < 80;y++){
for(int i = 0;i < 80;i++){
if(TestSlot && GetSlot->SerialNumber[y] == SerialNumber){
database.DeleteTraderItem(this->CharacterID(),y);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, GetSlot->SerialNumber[y]);
if(TestSlot && TraderItems->SerialNumber[i] == SerialNumber)
{
database.DeleteTraderItem(this->CharacterID(),i);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, TraderItems->SerialNumber[i], TraderItems->ItemID[i]);
TestSlot=false;
}
else if(GetSlot->ItemID[y] > 0)
else if (TraderItems->ItemID[i] > 0)
{
Count++;
}
}
if(Count == 0)
if (Count == 0)
{
Trader_EndTrader();
}
return;
}
else if(item) {
else
{
database.UpdateTraderItemCharges(this->CharacterID(), item->GetSerialNumber(), Charges-Quantity);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, item->GetSerialNumber());
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, item->GetSerialNumber(), item->GetID());
return;
@ -1426,22 +1481,38 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client*
Quantity,this->GetName());
}
void Client::ReturnTraderReq(const EQApplicationPacket* app, int16 TraderItemCharges){
void Client::ReturnTraderReq(const EQApplicationPacket* app, int16 TraderItemCharges, uint32 itemid){
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct));
EQApplicationPacket* outapp = nullptr;
if (GetClientVersion() >= ClientVersion::RoF)
{
outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderBuy_Struct));
}
else
{
outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct));
}
TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer;
memcpy(outtbs, tbs, app->size);
outtbs->Price = (tbs->Price * static_cast<uint32>(TraderItemCharges));
if (GetClientVersion() >= ClientVersion::RoF)
{
// Convert Serial Number back to Item ID for RoF+
outtbs->ItemID = itemid;
}
else
{
// RoF+ requires individual price, but older clients require total price
outtbs->Price = (tbs->Price * static_cast<uint32>(TraderItemCharges));
}
outtbs->Quantity = TraderItemCharges;
// This should probably be trader ID, not customer ID as it is below.
outtbs->TraderID = this->GetID();
outtbs->AlreadySold = 0;
QueuePacket(outapp);
@ -1478,7 +1549,7 @@ static void BazaarAuditTrail(const char *seller, const char *buyer, const char *
database.QueryDatabase(query);
}
void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicationPacket* app){
void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplicationPacket* app){
if(!Trader) return;
@ -1487,13 +1558,34 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
return;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader,sizeof(TraderBuy_Struct));
EQApplicationPacket* outapp = nullptr;
if (Trader->GetClientVersion() >= ClientVersion::RoF)
{
//outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderBuy_Struct));
}
else
{
//outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct));
}
outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct));
TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer;
outtbs->ItemID = tbs->ItemID;
const ItemInst* BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID);
const ItemInst* BuyItem = nullptr;
uint32 ItemID = 0;
if (GetClientVersion() >= ClientVersion::RoF)
{
// Convert Item ID to Serial Number for RoF+
ItemID = tbs->ItemID;
tbs->ItemID = Trader->FindTraderItemSerialNumber(tbs->ItemID);
}
BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID);
if(!BuyItem) {
Log.Out(Logs::Detail, Logs::Trading, "Unable to find item on trader.");
@ -1543,12 +1635,10 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
return;
}
ReturnTraderReq(app, outtbs->Quantity);
ReturnTraderReq(app, outtbs->Quantity, ItemID);
outtbs->TraderID = this->GetID();
outtbs->Action = BazaarBuyItem;
strn0cpy(outtbs->ItemName, BuyItem->GetItem()->Name, 64);
int TraderSlot = 0;
@ -1558,54 +1648,50 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
else
SendTraderItem(BuyItem->GetItem()->ID, BuyItem->GetCharges());
EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct));
MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp2->pBuffer;
// This cannot overflow assuming MAX_TRANSACTION_VALUE, checked above, is the default of 2000000000
uint32 TotalCost = tbs->Price * outtbs->Quantity;
outtbs->Price = TotalCost;
if (Trader->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ uses individual item price where older clients use total price
outtbs->Price = tbs->Price;
}
else
{
outtbs->Price = TotalCost;
}
this->TakeMoneyFromPP(TotalCost);
mus->platinum = TotalCost / 1000;
Log.Out(Logs::Detail, Logs::Trading, "Customer Paid: %d in Copper", TotalCost);
TotalCost -= (mus->platinum * 1000);
uint32 platinum = TotalCost / 1000;
TotalCost -= (platinum * 1000);
uint32 gold = TotalCost / 100;
TotalCost -= (gold * 100);
uint32 silver = TotalCost / 10;
TotalCost -= (silver * 10);
uint32 copper = TotalCost;
mus->gold = TotalCost / 100;
Trader->AddMoneyToPP(copper, silver, gold, platinum, true);
TotalCost -= (mus->gold * 100);
mus->silver = TotalCost / 10;
TotalCost -= (mus->silver * 10);
mus->copper = TotalCost;
Trader->AddMoneyToPP(mus->copper,mus->silver,mus->gold,mus->platinum,false);
mus->platinum = Trader->GetPlatinum();
mus->gold = Trader->GetGold();
mus->silver = Trader->GetSilver();
mus->copper = Trader->GetCopper();
Log.Out(Logs::Detail, Logs::Trading, "Trader Received: %d Platinum, %d Gold, %d Silver, %d Copper", platinum, gold, silver, copper);
TraderSlot = Trader->FindTraderItem(tbs->ItemID, outtbs->Quantity);
Trader->QueuePacket(outapp2);
if(RuleB(Bazaar, AuditTrail))
BazaarAuditTrail(Trader->GetName(), GetName(), BuyItem->GetItem()->Name, outtbs->Quantity, outtbs->Price, 0);
Trader->FindAndNukeTraderItem(tbs->ItemID, outtbs->Quantity, this, 0);
if (ItemID > 0 && Trader->GetClientVersion() >= ClientVersion::RoF)
{
// Convert Serial Number back to ItemID for RoF+
outtbs->ItemID = ItemID;
}
Trader->QueuePacket(outapp);
safe_delete(outapp);
safe_delete(outapp2);
}
void Client::SendBazaarWelcome()
@ -1996,6 +2082,15 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St
for(int i = 0; i < 80; i++) {
if(gis->ItemID[i] == ItemID) {
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = gis->ItemID[i];
}
else
{
tdis->ItemID = gis->SerialNumber[i];
}
tdis->ItemID = gis->SerialNumber[i];
Log.Out(Logs::Detail, Logs::Trading, "Telling customer to remove item %i with %i charges and S/N %i",
ItemID, Charges, gis->SerialNumber[i]);