[Fix] Parcel Delivery Updates (#4688)

* Fix two parcel bugs

Fix two Parcel Bugs

- If a player was at their parcel limit and perform a bazaar purchase via parcel delivery, their money would be lost
- If a container with items was delivered via parcel, the parcel under certain inventory conditions could be delivered into an incorrect slot resulting in the container being lost.

* Incorrect field used for BagSize vs ItemSize.  Silly mistake.

* Remove duplicate check and reorder stacking check

* Fix edge case when Parcel Window remains open and Bazaar purchases are made.

* Repair
- bazaar purchase of items with charges reverting to 1 charge in error
- bazaar visual error with selling price. Was caused by the parcel fee not being properly reflected in the client
- corrected a type mismatch with parcel fee uint32 vs uin64
- corrected a few TraderPurchase and TraderSell event data points by splitting quantity and charges

* Formatting

* Use pre-existing AddMoney and TakeMoney and remove unnecessary routines

* Updates after rebase
This commit is contained in:
Mitch Freeman 2025-02-15 20:27:09 -04:00 committed by GitHub
parent c09fad5a75
commit ab4e1191ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 210 additions and 277 deletions

View File

@ -882,8 +882,9 @@ namespace PlayerEvent {
uint32 trader_id;
std::string trader_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint32 quantity;
int32 charges;
uint64 total_cost;
uint64 player_money_balance;
@ -903,6 +904,7 @@ namespace PlayerEvent {
CEREAL_NVP(trader_id),
CEREAL_NVP(trader_name),
CEREAL_NVP(price),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
@ -922,8 +924,9 @@ namespace PlayerEvent {
uint32 buyer_id;
std::string buyer_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint32 quantity;
int32 charges;
uint64 total_cost;
uint64 player_money_balance;
@ -943,6 +946,7 @@ namespace PlayerEvent {
CEREAL_NVP(buyer_id),
CEREAL_NVP(buyer_name),
CEREAL_NVP(price),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
@ -1152,6 +1156,7 @@ namespace PlayerEvent {
uint32 augment_5_id;
uint32 augment_6_id;
uint32 quantity;
int32 charges;
std::string from_player_name;
std::string to_player_name;
uint32 sent_date;
@ -1169,6 +1174,7 @@ namespace PlayerEvent {
CEREAL_NVP(augment_5_id),
CEREAL_NVP(augment_6_id),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(from_player_name),
CEREAL_NVP(to_player_name),
CEREAL_NVP(sent_date)

View File

@ -2028,3 +2028,57 @@ int16 EQ::InventoryProfile::_HasEvolvingItem(ItemInstQueue &iqueue, uint64 evolv
return INVALID_INDEX;
}
int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItemWithStacking(ItemInstance *item_inst) const
{
auto item_data = item_inst->GetItem();
if (!item_data) {
return INVALID_INDEX;
}
for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) {
auto const inv_item = GetItem(i);
if (!inv_item) {
// Found available slot in personal inventory
// Anything will fit here
return i;
}
if (item_data->IsClassBag() && item_inst->IsNoneEmptyContainer()) {
// If the inbound item is a bag with items, it cannot be stored within a bag
// Move to next potential slot
continue;
}
if (inv_item->GetID() == item_data->ID && item_data->Stackable) {
if (item_inst->GetCharges() + inv_item->GetCharges() <= item_data->StackSize) {
// Found a personal inventory slot that has room for a stackable addition
return i;
}
}
if (inv_item->IsClassBag() && CanItemFitInContainer(item_data, inv_item->GetItem())) {
int16 const base_slot_id = CalcSlotId(i, invbag::SLOT_BEGIN);
uint8 const bag_size = inv_item->GetItem()->BagSlots;
uint8 const item_size = item_data->Size;
for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
auto bag_item = GetItem(base_slot_id + bag_slot);
if (!bag_item) {
// Found available slot within bag that will hold inbound item
return base_slot_id + bag_slot;
}
if (bag_item && item_data->Stackable && bag_item->GetID() == item_data->ID) {
if (item_inst->GetCharges() + bag_item->GetCharges() <= item_data->StackSize) {
// Found a bag slot has room for a stackable addition
return base_slot_id + bag_slot;
}
}
}
}
}
return INVALID_INDEX;
}

View File

@ -179,6 +179,7 @@ namespace EQ
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
int16 FindFirstFreeSlotThatFitsItemWithStacking(ItemInstance *inst) const;
// Calculate slot_id for an item within a bag
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id

View File

@ -12969,107 +12969,6 @@ void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity)
}
}
void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client)
{
//I noticed in the ROF2 client that the client auto updates the currency values using overflow
//Therefore, I created this method to ensure that the db matches and clients don't see 10 pp 5 gp
//becoming 9pp 15 gold with the current AddMoneyToPP method.
auto add_pp = copper / 1000;
auto add_gp = (copper - add_pp * 1000) / 100;
auto add_sp = (copper - add_pp * 1000 - add_gp * 100) / 10;
auto add_cp = copper - add_pp * 1000 - add_gp * 100 - add_sp * 10;
m_pp.copper += add_cp;
if (m_pp.copper >= 10) {
m_pp.silver += m_pp.copper / 10;
m_pp.copper = m_pp.copper % 10;
}
m_pp.silver += add_sp;
if (m_pp.silver >= 10) {
m_pp.gold += m_pp.silver / 10;
m_pp.silver = m_pp.silver % 10;
}
m_pp.gold += add_gp;
if (m_pp.gold >= 10) {
m_pp.platinum += m_pp.gold / 10;
m_pp.gold = m_pp.gold % 10;
}
m_pp.platinum += add_pp;
if (update_client) {
SendMoneyUpdate();
}
RecalcWeight();
SaveCurrency();
LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]",
GetName(),
m_pp.platinum,
m_pp.gold,
m_pp.silver,
m_pp.copper
);
}
bool Client::TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client)
{
int32 remove_pp = copper / 1000;
int32 remove_gp = (copper - remove_pp * 1000) / 100;
int32 remove_sp = (copper - remove_pp * 1000 - remove_gp * 100) / 10;
int32 remove_cp = copper - remove_pp * 1000 - remove_gp * 100 - remove_sp * 10;
uint64 current_money = GetCarriedMoney();
if (copper > current_money) {
return false; //client does not have enough money on them
}
m_pp.copper -= remove_cp;
if (m_pp.copper < 0) {
m_pp.silver -= 1;
m_pp.copper = m_pp.copper + 10;
if (m_pp.copper >= 10) {
m_pp.silver += m_pp.copper / 10;
m_pp.copper = m_pp.copper % 10;
}
}
m_pp.silver -= remove_sp;
if (m_pp.silver < 0) {
m_pp.gold -= 1;
m_pp.silver = m_pp.silver + 10;
if (m_pp.silver >= 10) {
m_pp.gold += m_pp.silver / 10;
m_pp.silver = m_pp.silver % 10;
}
}
m_pp.gold -= remove_gp;
if (m_pp.gold < 0) {
m_pp.platinum -= 1;
m_pp.gold = m_pp.gold + 10;
if (m_pp.gold >= 10) {
m_pp.platinum += m_pp.gold / 10;
m_pp.gold = m_pp.gold % 10;
}
}
m_pp.platinum -= remove_pp;
if (update_client) {
SendMoneyUpdate();
}
SaveCurrency();
RecalcWeight();
return true;
}
void Client::SendTopLevelInventory()
{
EQ::ItemInstance* inst = nullptr;

View File

@ -879,11 +879,9 @@ public:
void QuestReadBook(const char* text, uint8 type);
void SendMoneyUpdate();
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
bool TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client);
bool TakePlatinum(uint32 platinum, bool update_client = false);
void AddMoneyToPP(uint64 copper, bool update_client = false);
void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client = false);
void AddMoneyToPPWithOverflow(uint64 copper, bool update_client);
void AddPlatinum(uint32 platinu, bool update_client = false);
bool HasMoney(uint64 copper);
uint64 GetCarriedMoney();

View File

@ -632,7 +632,13 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
return;
}
auto p = m_parcels.find(parcel_in.parcel_slot_id);
auto p = std::find_if(
m_parcels.begin(),
m_parcels.end(),
[&](const std::pair<uint32, CharacterParcelsRepository::CharacterParcels> &x) {
return x.first == parcel_in.parcel_slot_id && x.second.item_id == parcel_in.parcel_item_id;
}
);
if (p != m_parcels.end()) {
uint32 item_id = parcel_in.parcel_item_id;
uint32 item_quantity = p->second.quantity;
@ -656,25 +662,70 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
p->second.aug_slot_6
)
);
if (!inst) {
SendParcelRetrieveAck();
return;
}
if (inst->IsStackable()) {
inst->SetCharges(item_quantity > 0 ? item_quantity : 1);
}
switch (parcel_in.parcel_item_id) {
case PARCEL_MONEY_ITEM_ID: {
AddMoneyToPP(p->second.quantity, true);
MessageString(
Chat::Yellow,
PARCEL_DELIVERED,
merchant->GetCleanName(),
"Money",
p->second.from_name.c_str()
Chat::Yellow, PARCEL_DELIVERED, merchant->GetCleanName(), "Money", p->second.from_name.c_str()
);
break;
}
default: {
auto free_id = GetInv().FindFreeSlot(false, false);
std::vector<CharacterParcelsContainersRepository::CharacterParcelsContainers> results{};
if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) {
auto contents = inst->GetContents();
results = CharacterParcelsContainersRepository::GetWhere(
database, fmt::format("`parcels_id` = {}", p->second.id)
);
for (auto i: results) {
auto item = database.CreateItem(
i.item_id,
i.quantity,
i.aug_slot_1,
i.aug_slot_2,
i.aug_slot_3,
i.aug_slot_4,
i.aug_slot_5,
i.aug_slot_6
);
if (!item) {
SendParcelRetrieveAck();
return;
}
if (CheckLoreConflict(item->GetItem())) {
if (RuleB(Parcel, DeleteOnDuplicate)) {
MessageString(Chat::Yellow, PARCEL_DUPLICATE_DELETE, inst->GetItem()->Name);
continue;
}
MessageString(Chat::Yellow, DUP_LORE);
SendParcelRetrieveAck();
return;
}
contents->emplace(i.slot_id, item);
}
}
auto const free_id = GetInv().FindFirstFreeSlotThatFitsItemWithStacking(inst.get());
if (free_id == INVALID_INDEX) {
SendParcelRetrieveAck();
MessageString(Chat::White, PARCEL_INV_FULL, merchant->GetCleanName());
return;
}
if (CheckLoreConflict(inst->GetItem())) {
if (RuleB(Parcel, DeleteOnDuplicate)) {
MessageString(Chat::Yellow, PARCEL_DUPLICATE_DELETE, inst->GetItem()->Name);
@ -685,108 +736,33 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
return;
}
}
else if (inst->IsStackable()) {
inst->SetCharges(item_quantity);
if (TryStacking(inst.get(), ItemPacketTrade, true, false)) {
MessageString(
Chat::Yellow,
PARCEL_DELIVERED_2,
merchant->GetCleanName(),
std::to_string(item_quantity).c_str(),
inst->GetItem()->Name,
p->second.from_name.c_str()
);
}
else if (free_id != INVALID_INDEX) {
inst->SetCharges(item_quantity);
if (PutItemInInventory(free_id, *inst, true)) {
MessageString(
Chat::Yellow,
PARCEL_DELIVERED_2,
merchant->GetCleanName(),
std::to_string(item_quantity).c_str(),
inst->GetItem()->Name,
p->second.from_name.c_str()
);
}
}
else {
MessageString(Chat::Yellow, PARCEL_INV_FULL, merchant->GetCleanName());
SendParcelRetrieveAck();
return;
}
if (AutoPutLootInInventory(*inst.get(), false, true)) {
MessageString(
Chat::Yellow,
PARCEL_DELIVERED_2,
merchant->GetCleanName(),
std::to_string(item_quantity).c_str(),
inst->GetItem()->Name,
p->second.from_name.c_str()
);
}
else if (free_id != INVALID_INDEX) {
std::vector<CharacterParcelsContainersRepository::CharacterParcelsContainers> results{};
if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) {
results = CharacterParcelsContainersRepository::GetWhere(database, fmt::format("`parcels_id` = {}", p->second.id));
for (auto const &i : results) {
std::unique_ptr<EQ::ItemInstance> item(
database.CreateItem(
i.item_id,
i.quantity,
i.aug_slot_1,
i.aug_slot_2,
i.aug_slot_3,
i.aug_slot_4,
i.aug_slot_5,
i.aug_slot_6
)
);
if (CheckLoreConflict(item->GetItem())) {
Message(
Chat::Yellow,
fmt::format("Lore Item Found in Inventory: {}", item->GetItem()->Name).c_str());
MessageString(Chat::Yellow, DUP_LORE);
Message(Chat::Red, "Unable to retrieve parcel.");
SendParcelRetrieveAck();
return;
}
}
}
inst->SetCharges(item_quantity > 0 ? item_quantity : 1);
if (PutItemInInventory(free_id, *inst.get(), true)) {
if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) {
for (auto const &i: results) {
std::unique_ptr<EQ::ItemInstance> item(
database.CreateItem(
i.item_id,
i.quantity,
i.aug_slot_1,
i.aug_slot_2,
i.aug_slot_3,
i.aug_slot_4,
i.aug_slot_5,
i.aug_slot_6
)
);
auto bag_slot = EQ::InventoryProfile::CalcSlotId(free_id, i.slot_id);
PutItemInInventory(bag_slot, *item.get(), true);
}
}
MessageString(
Chat::Yellow,
PARCEL_DELIVERED,
merchant->GetCleanName(),
inst->GetItem()->Name,
p->second.from_name.c_str()
);
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) {
PlayerEvent::ParcelRetrieve e{};
e.from_player_name = p->second.from_name;
e.item_id = p->second.item_id;
e.augment_1_id = p->second.aug_slot_1;
e.augment_2_id = p->second.aug_slot_2;
e.augment_3_id = p->second.aug_slot_3;
e.augment_4_id = p->second.aug_slot_4;
e.augment_5_id = p->second.aug_slot_5;
e.augment_6_id = p->second.aug_slot_6;
e.quantity = p->second.quantity;
e.sent_date = p->second.sent_date;
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) {
PlayerEvent::ParcelRetrieve e{};
e.from_player_name = p->second.from_name;
e.item_id = p->second.item_id;
e.augment_1_id = p->second.aug_slot_1;
e.augment_2_id = p->second.aug_slot_2;
e.augment_3_id = p->second.aug_slot_3;
e.augment_4_id = p->second.aug_slot_4;
e.augment_5_id = p->second.aug_slot_5;
e.augment_6_id = p->second.aug_slot_6;
e.quantity = p->second.quantity;
e.sent_date = p->second.sent_date;
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
for (auto const &i:results) {
for (auto const &i:results) {
e.from_player_name = p->second.from_name;
e.item_id = i.item_id;
e.augment_1_id = i.aug_slot_1;
@ -798,19 +774,9 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
e.quantity = i.quantity;
e.sent_date = p->second.sent_date;
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
}
}
}
else {
MessageString(Chat::Yellow, PARCEL_INV_FULL, merchant->GetCleanName());
SendParcelRetrieveAck();
return;
}
}
else {
MessageString(Chat::Yellow, PARCEL_INV_FULL, merchant->GetCleanName());
SendParcelRetrieveAck();
return;
}
}
}
@ -819,6 +785,7 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
SendParcelDelete(parcel_in);
m_parcels.erase(p);
}
SendParcelRetrieveAck();
SendParcelIconStatus();
}
@ -831,7 +798,6 @@ bool Client::DeleteParcel(uint32 parcel_id)
return false;
}
auto it = std::find_if(m_parcels.cbegin(), m_parcels.cend(), [&](const auto &x) { return x.second.id == parcel_id; });
SetParcelCount(GetParcelCount() - 1);
return true;

View File

@ -1458,12 +1458,13 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic
.trader_id = Trader->CharacterID(),
.trader_name = Trader->GetCleanName(),
.price = tbs->price,
.charges = outtbs->quantity,
.quantity = outtbs->quantity,
.charges = buy_item->GetCharges(),
.total_cost = (tbs->price * outtbs->quantity),
.player_money_balance = GetCarriedMoney(),
};
RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e);
RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e);
}
if (player_event_logs.IsEventEnabled(PlayerEvent::TRADER_SELL)) {
@ -1479,7 +1480,8 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic
.buyer_id = CharacterID(),
.buyer_name = GetCleanName(),
.price = tbs->price,
.charges = outtbs->quantity,
.quantity = outtbs->quantity,
.charges = buy_item->GetCharges(),
.total_cost = (tbs->price * outtbs->quantity),
.player_money_balance = Trader->GetCarriedMoney(),
};
@ -1960,8 +1962,8 @@ void Client::SellToBuyer(const EQApplicationPacket *app)
}
uint64 total_cost = (uint64) sell_line.item_cost * (uint64) sell_line.seller_quantity;
AddMoneyToPPWithOverflow(total_cost, false);
buyer->TakeMoneyFromPPWithOverFlow(total_cost, false);
AddMoneyToPP(total_cost, false);
buyer->TakeMoneyFromPP(total_cost, false);
if (player_event_logs.IsEventEnabled(PlayerEvent::BARTER_TRANSACTION)) {
PlayerEvent::BarterTransaction e{};
@ -2879,6 +2881,21 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
return;
}
auto next_slot = FindNextFreeParcelSlot(CharacterID());
if (next_slot == INVALID_INDEX) {
LogTrading(
"{} attempted to purchase {} from the bazaar with parcel delivery. Unfortunately their parcel limit was reached. "
"Purchase unsuccessful.",
GetCleanName(),
buy_item->GetItem()->Name
);
in->method = BazaarByParcel;
in->sub_action = TooManyParcels;
TraderRepository::UpdateActiveTransaction(database, trader_item.id, false);
TradeRequestFailed(app);
return;
}
LogTrading(
"Name: <green>[{}] IsStackable: <green>[{}] Requested Quantity: <green>[{}] Charges on Item <green>[{}]",
buy_item->GetItem()->Name,
@ -2888,23 +2905,24 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
);
// Determine the actual quantity for the purchase
int32 charges = static_cast<int32>(tbs->quantity);
if (!buy_item->IsStackable()) {
tbs->quantity = 1;
}
else {
int32 item_charges = buy_item->GetCharges();
if (item_charges <= 0) {
tbs->quantity = 1;
if (buy_item->GetCharges() <= 0) {
charges = 1;
}
else if (static_cast<uint32>(item_charges) < tbs->quantity) {
tbs->quantity = item_charges;
else {
charges = buy_item->GetCharges();
}
}
LogTrading("Actual quantity that will be traded is <green>[{}]", tbs->quantity);
LogTrading(
"Actual quantity that will be traded is <green>[{}] {}",
tbs->quantity,
buy_item->GetCharges() ? fmt::format("with {} charges", buy_item->GetCharges()) : ""
);
uint64 total_transaction_value = static_cast<uint64>(tbs->price) * static_cast<uint64>(tbs->quantity);
if (total_transaction_value > MAX_TRANSACTION_VALUE) {
uint64 total_cost = static_cast<uint64>(tbs->price) * static_cast<uint64>(tbs->quantity);
if (total_cost > MAX_TRANSACTION_VALUE) {
Message(
Chat::Red,
"That would exceed the single transaction limit of %u platinum.",
@ -2915,9 +2933,8 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
return;
}
uint32 total_cost = tbs->price * tbs->quantity;
uint32 fee = static_cast<uint32>(std::round((uint32) total_cost * RuleR(Bazaar, ParcelDeliveryCostMod)));
if (!TakeMoneyFromPP(total_cost + fee)) {
uint64 fee = std::round(total_cost * RuleR(Bazaar, ParcelDeliveryCostMod));
if (!TakeMoneyFromPP(total_cost + fee, false)) {
RecordPlayerEventLog(
PlayerEvent::POSSIBLE_HACK,
PlayerEvent::PossibleHackEvent{
@ -2951,7 +2968,8 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
.trader_id = tbs->trader_id,
.trader_name = tbs->seller_name,
.price = tbs->price,
.charges = tbs->quantity,
.quantity = tbs->quantity,
.charges = buy_item->IsStackable() ? 1 : charges,
.total_cost = total_cost,
.player_money_balance = GetCarriedMoney(),
};
@ -2960,24 +2978,10 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
}
CharacterParcelsRepository::CharacterParcels parcel_out{};
auto next_slot = FindNextFreeParcelSlot(CharacterID());
if (next_slot == INVALID_INDEX) {
LogTrading(
"{} attempted to purchase {} from the bazaar with parcel delivery. Unfortunately their parcel limit was reached. "
"Purchase unsuccessful.",
GetCleanName(),
buy_item->GetItem()->Name
);
in->method = BazaarByParcel;
in->sub_action = TooManyParcels;
TraderRepository::UpdateActiveTransaction(database, trader_item.id, false);
TradeRequestFailed(app);
return;
}
parcel_out.from_name = tbs->seller_name;
parcel_out.note = "Delivered from a Bazaar Purchase";
parcel_out.sent_date = time(nullptr);
parcel_out.quantity = buy_item->IsStackable() ? tbs->quantity : buy_item->GetCharges();
parcel_out.quantity = charges;
parcel_out.item_id = buy_item->GetItem()->ID;
parcel_out.aug_slot_1 = buy_item->GetAugmentItemID(0);
parcel_out.aug_slot_2 = buy_item->GetAugmentItemID(1);
@ -3017,7 +3021,8 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
e.augment_4_id = parcel_out.aug_slot_4;
e.augment_5_id = parcel_out.aug_slot_5;
e.augment_6_id = parcel_out.aug_slot_6;
e.quantity = parcel_out.quantity;
e.quantity = tbs->quantity;
e.charges = buy_item->IsStackable() ? 1 : charges;
e.sent_date = parcel_out.sent_date;
RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e);
@ -3060,6 +3065,8 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
strn0cpy(out_data->trader_buy_struct.buyer_name, GetCleanName(), sizeof(out_data->trader_buy_struct.buyer_name));
worldserver.SendPacket(out_server.get());
SendMoneyUpdate();
}
void Client::SetBuyerWelcomeMessage(const char *welcome_message)

View File

@ -4002,21 +4002,23 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
TraderRepository::UpdateActiveTransaction(database, in->id, false);
auto item = trader_pc->FindTraderItemBySerialNumber(item_sn);
if (player_event_logs.IsEventEnabled(PlayerEvent::TRADER_SELL)) {
auto buy_item = trader_pc->FindTraderItemBySerialNumber(item_sn);
auto e = PlayerEvent::TraderSellEvent{
.item_id = in->trader_buy_struct.item_id,
.augment_1_id = buy_item->GetAugmentItemID(0),
.augment_2_id = buy_item->GetAugmentItemID(1),
.augment_3_id = buy_item->GetAugmentItemID(2),
.augment_4_id = buy_item->GetAugmentItemID(3),
.augment_5_id = buy_item->GetAugmentItemID(4),
.augment_6_id = buy_item->GetAugmentItemID(5),
auto e = PlayerEvent::TraderSellEvent{
.item_id = item ? item->GetID() : 0,
.augment_1_id = item->GetAugmentItemID(0),
.augment_2_id = item->GetAugmentItemID(1),
.augment_3_id = item->GetAugmentItemID(2),
.augment_4_id = item->GetAugmentItemID(3),
.augment_5_id = item->GetAugmentItemID(4),
.augment_6_id = item->GetAugmentItemID(5),
.item_name = in->trader_buy_struct.item_name,
.buyer_id = in->buyer_id,
.buyer_name = in->trader_buy_struct.buyer_name,
.price = in->trader_buy_struct.price,
.charges = in->trader_buy_struct.quantity,
.quantity = in->trader_buy_struct.quantity,
.charges = item ? item->IsStackable() ? 1 : item->GetCharges() : 0,
.total_cost = (in->trader_buy_struct.price * in->trader_buy_struct.quantity),
.player_money_balance = trader_pc->GetCarriedMoney(),
};
@ -4121,7 +4123,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
sell_line.seller_quantity,
sell_line.item_name,
buyer->GetCleanName());
buyer->AddMoneyToPPWithOverflow(total_cost, true);
buyer->AddMoneyToPP(total_cost, true);
buyer->RemoveItem(sell_line.item_id, sell_line.seller_quantity);
buyer->Message(
@ -4220,7 +4222,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
if (inst->IsStackable()) {
if (!buyer->PutItemInInventoryWithStacking(inst.get())) {
buyer->Message(Chat::Red, "Error putting item in your inventory.");
buyer->AddMoneyToPPWithOverflow(total_cost, true);
buyer->AddMoneyToPP(total_cost, true);
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
@ -4232,7 +4234,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
inst->SetCharges(1);
if (!buyer->PutItemInInventoryWithStacking(inst.get())) {
buyer->Message(Chat::Red, "Error putting item in your inventory.");
buyer->AddMoneyToPPWithOverflow(total_cost, true);
buyer->AddMoneyToPP(total_cost, true);
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
@ -4241,7 +4243,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
}
}
if (!buyer->TakeMoneyFromPPWithOverFlow(total_cost, false)) {
if (!buyer->TakeMoneyFromPP(total_cost, false)) {
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
@ -4306,7 +4308,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
uint64 total_cost = (uint64) sell_line.item_cost * (uint64) sell_line.seller_quantity;
seller->RemoveItem(in->buy_item_id, in->seller_quantity);
seller->AddMoneyToPPWithOverflow(total_cost, false);
seller->AddMoneyToPP(total_cost, false);
seller->SendBarterBuyerClientMessage(
sell_line,
Barter_SellerTransactionComplete,