(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
+1
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;
+4 -2
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
+99 -57
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)
+167 -72
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]);