diff --git a/common/bazaar.cpp b/common/bazaar.cpp index c3c828be6..2943a9b93 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -187,11 +187,11 @@ Bazaar::GetSearchResults( ); } else { - search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + search_criteria_trader.append(fmt::format(" AND trader.character_id = {}", search.trader_id)); } } else { - search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + search_criteria_trader.append(fmt::format(" AND trader.character_id = {}", search.trader_id)); } } @@ -297,8 +297,8 @@ Bazaar::GetSearchResults( BazaarSearchResultsFromDB_Struct r{}; r.count = 1; - r.trader_id = t.trader.char_id; - r.serial_number = t.trader.item_sn; + r.trader_id = t.trader.character_id; + r.item_unique_id = t.trader.item_unique_id; r.cost = t.trader.item_cost; r.slot_id = t.trader.slot_id; r.charges = t.trader.item_charges; @@ -307,7 +307,6 @@ Bazaar::GetSearchResults( r.trader_zone_id = t.trader.char_zone_id; r.trader_zone_instance_id = t.trader.char_zone_instance_id; r.trader_entity_id = t.trader.char_entity_id; - r.serial_number_RoF = t.trader.item_sn; r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name); r.trader_name = fmt::format("{:.63}\0", t.trader_name); r.item_stat = item_results.at(t.trader.item_id).stats; diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 976168c44..98e8128b6 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -7189,7 +7189,7 @@ ALTER TABLE `character_parcels_containers` ManifestEntry{ .version = 9329, .description = "2025_03_27_implement_item_unique_unique_id.sql", - .check = "SHOW COLUMNS FROM `inventory` LIKE 'serial_number'", + .check = "SHOW COLUMNS FROM `inventory` LIKE 'item_unique_id'", .condition = "empty", .match = "", .sql = R"( @@ -7220,6 +7220,23 @@ ALTER TABLE `inventory_snapshots` DROP PRIMARY KEY, ADD PRIMARY KEY (`time_index`, `character_id`, `slot_id`) USING BTREE; +ALTER TABLE `trader` + CHANGE COLUMN `char_id` `character_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`, + ADD COLUMN `item_unique_id` VARCHAR(16) NULL DEFAULT NULL AFTER `item_id`, + CHANGE COLUMN `aug_slot_1` `augment_one` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `item_unique_id`, + CHANGE COLUMN `aug_slot_2` `augment_two` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `augment_one`, + CHANGE COLUMN `aug_slot_3` `augment_three` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `augment_two`, + CHANGE COLUMN `aug_slot_4` `augment_four` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `augment_three`, + CHANGE COLUMN `aug_slot_5` `augment_five` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `augment_four`, + CHANGE COLUMN `aug_slot_6` `augment_six` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `augment_five`, + DROP COLUMN `item_sn`, + DROP INDEX `idx_trader_item_sn`, + DROP INDEX `charid_slotid`, + ADD INDEX `charid_slotid` (`character_id`, `slot_id`) USING BTREE, + DROP INDEX `idx_trader_char`, + ADD INDEX `idx_trader_char` (`character_id`, `char_zone_id`, `char_zone_instance_id`) USING BTREE, + ADD UNIQUE INDEX `idx_item_unique_id` (`item_unique_id`); + )", .content_schema_update = false }, diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 9c537759b..71495ac37 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3093,7 +3093,7 @@ struct BazaarSearchCriteria_Struct { struct BazaarInspect_Struct { uint32 action; char player_name[64]; - char serial_number[16]; + char item_unique_id[17]; uint32 item_id; uint32 trader_id; }; @@ -3730,7 +3730,7 @@ struct Trader_Struct { }; struct TraderItems_Struct { - std::string serial_number; + std::string item_unique_id; uint32 item_id; uint64 item_cost; @@ -3738,7 +3738,7 @@ struct TraderItems_Struct { void serialize(Archive &archive) { archive( - CEREAL_NVP(serial_number), + CEREAL_NVP(item_unique_id), CEREAL_NVP(item_id), CEREAL_NVP(item_cost) ); @@ -3850,7 +3850,7 @@ struct TraderBuy_Struct { /*084*/ char seller_name[64]; /*148*/ char unknown_148[32]; /*180*/ char item_name[64]; -/*244*/ char serial_number[17]; +/*244*/ char item_unique_id[17]; /*261*/ char unknown_261[3]; /*264*/ uint32 item_id; /*268*/ uint32 price; @@ -3869,12 +3869,12 @@ struct TraderItemUpdate_Struct{ }; struct TraderPriceUpdate_Struct { -/*000*/ uint32 Action; -/*004*/ uint32 SubAction; -/*008*/ char serial_number[16]; -/*012*/ uint32 Unknown012; -/*016*/ uint32 NewPrice; -/*020*/ uint32 Unknown016; +/*000*/ uint32 action; +/*002*/ uint32 sub_action; +/*004*/ char item_unique_id[17]; +/*021*/ char unknown_021[3]; +/*024*/ uint32 unknown_024; +/*028*/ uint32 new_price; }; struct MoneyUpdate_Struct{ @@ -3889,6 +3889,7 @@ struct TraderDelItem_Struct{ uint32 trader_id; uint32 item_id; uint32 unknown_012; + char item_unique_id[17]; }; struct TraderClick_Struct{ @@ -6494,7 +6495,7 @@ struct BazaarSearchResultsFromDB_Struct { uint32 count; uint32 trader_id; uint32 item_id; - std::string serial_number; + std::string item_unique_id; uint32 charges; uint32 cost; uint32 slot_id; @@ -6506,7 +6507,6 @@ struct BazaarSearchResultsFromDB_Struct { uint32 item_stat; bool stackable; std::string item_name; - std::string serial_number_RoF; std::string trader_name; template @@ -6516,7 +6516,7 @@ struct BazaarSearchResultsFromDB_Struct { CEREAL_NVP(count), CEREAL_NVP(trader_id), CEREAL_NVP(item_id), - CEREAL_NVP(serial_number), + CEREAL_NVP(item_unique_id), CEREAL_NVP(charges), CEREAL_NVP(cost), CEREAL_NVP(slot_id), @@ -6528,7 +6528,6 @@ struct BazaarSearchResultsFromDB_Struct { CEREAL_NVP(item_stat), CEREAL_NVP(stackable), CEREAL_NVP(item_name), - CEREAL_NVP(serial_number_RoF), CEREAL_NVP(trader_name) ); } diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 1f2c1af76..c3a246936 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -159,7 +159,7 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_timers = copy.m_timers; if (copy.GetUniqueID().empty()) { - LogError("Creating Serial Number as part of Clone command"); + LogError("Creating unique item ID as part of clone process for item id {}", copy.GetID()); copy.CreateUniqueID(); } m_unique_id = copy.m_unique_id; diff --git a/common/item_instance.h b/common/item_instance.h index d11020406..b30836325 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -168,6 +168,14 @@ namespace EQ int16 GetCharges() const { return m_charges; } void SetCharges(int16 charges) { m_charges = charges; } + int16 GetQuantityFromCharges() const + { + if (GetCharges() > 0 || IsStackable() || GetItem()->MaxCharges > 0) { + return GetCharges(); + } + + return 1; + } uint32 GetPrice() const { return m_price; } void SetPrice(uint32 price) { m_price = price; } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 53da0937c..5191fa692 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -480,7 +480,7 @@ namespace RoF2 for (auto i: results) { VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.trader_id); //trader ID - VARSTRUCT_ENCODE_STRING(bufptr, i.serial_number_RoF.c_str()); //serial + VARSTRUCT_ENCODE_STRING(bufptr, i.item_unique_id.c_str()); //serial VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.cost); //cost VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.stackable ? i.charges : i.count); //quantity VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_id); //ID @@ -757,7 +757,7 @@ namespace RoF2 ar(bl); //packet size - auto packet_size = bl.item_name.length() + 1 + 34; + uint32 packet_size = bl.item_name.length() + 1 + 34; for (auto const &b: bl.trade_items) { packet_size += b.item_name.length() + 1; packet_size += 12; @@ -4130,17 +4130,19 @@ namespace RoF2 auto buffer = new char[4404]{}; // 4404 is the fixed size of the packet for 200 item limit of RoF2 auto pos = buffer; + auto pos_unique_id = buffer + 4; + auto pos_cost = buffer + 3604; VARSTRUCT_ENCODE_TYPE(uint32, pos, structs::RoF2BazaarTraderBuyerActions::ListTraderItems); for (auto const &t: tcm.items) { - strn0cpy(pos, t.serial_number.data(), t.serial_number.length() + 1); - pos += 3600; - VARSTRUCT_ENCODE_TYPE(uint32, pos, t.item_cost); - pos -= 3604 - 18; + strn0cpy(pos_unique_id, t.item_unique_id.data(), t.item_unique_id.length() + 1); + *(uint32 *) pos_cost = t.item_cost; + pos_unique_id += 18; + pos_cost += 4; } for (int i = tcm.items.size(); i < EQ::invtype::BAZAAR_SIZE; i++) { - strn0cpy(pos, "0000000000000000", 18); - pos += 18; + strn0cpy(pos_unique_id, "0000000000000000", 18); + pos_unique_id += 18; } safe_delete_array(in->pBuffer); @@ -4160,7 +4162,7 @@ namespace RoF2 } case PriceUpdate: { SETUP_DIRECT_ENCODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct); - switch (emu->SubAction) { + switch (emu->sub_action) { case BazaarPriceChange_AddItem: { auto outapp = std::make_unique( OP_Trader, @@ -4168,7 +4170,7 @@ namespace RoF2 ); auto data = (structs::TraderStatus_Struct *) outapp->pBuffer; - data->action = emu->Action; + data->action = emu->action; data->sub_action = BazaarPriceChange_AddItem; LogTrading( "(RoF2) PriceUpdate action [{}] AddItem subaction [{}]", @@ -4186,7 +4188,7 @@ namespace RoF2 ); auto data = (structs::TraderStatus_Struct *) outapp->pBuffer; - data->action = emu->Action; + data->action = emu->action; data->sub_action = BazaarPriceChange_RemoveItem; LogTrading( "(RoF2) PriceUpdate action [{}] RemoveItem subaction [{}]", @@ -4204,7 +4206,7 @@ namespace RoF2 ); auto data = (structs::TraderStatus_Struct *) outapp->pBuffer; - data->action = emu->Action; + data->action = emu->action; data->sub_action = BazaarPriceChange_UpdatePrice; LogTrading( "(RoF2) PriceUpdate action [{}] UpdatePrice subaction [{}]", @@ -4229,7 +4231,7 @@ namespace RoF2 "(RoF2) BuyTraderItem action [{}] item_id [{}] item_sn [{}] buyer [{}]", action, eq->item_id, - eq->serial_number, + eq->item_unique_id, eq->buyer_name ); dest->FastQueuePacket(&in); @@ -4268,8 +4270,7 @@ namespace RoF2 OUT_str(buyer_name); OUT_str(seller_name); OUT_str(item_name); - OUT_str(serial_number); - //strn0cpy(eq->serial_number, emu->serial_number.c_str(), sizeof(eq->serial_number)); + OUT_str(item_unique_id); FINISH_ENCODE(); } @@ -4279,15 +4280,13 @@ namespace RoF2 ENCODE_LENGTH_EXACT(TraderDelItem_Struct); SETUP_DIRECT_ENCODE(TraderDelItem_Struct, structs::TraderDelItem_Struct); LogTrading( - "(RoF2) trader_id [{}] item_id [{}]", + "(RoF2) trader_id [{}] item_unique_id [{}]", emu->trader_id, - emu->item_id + emu->item_unique_id ); - eq->TraderID = emu->trader_id; - auto serial = fmt::format("{:016}\n", emu->item_id); - strn0cpy(eq->SerialNumber, serial.c_str(), sizeof(eq->SerialNumber)); - LogTrading("(RoF2) TraderID [{}], SerialNumber: [{}]", emu->trader_id, emu->item_id); + eq->trader_id = emu->trader_id; + strn0cpy(eq->item_unique_id, emu->item_unique_id, sizeof(eq->item_unique_id)); FINISH_ENCODE(); } @@ -4334,8 +4333,7 @@ namespace RoF2 OUT_str(buyer_name); OUT_str(seller_name); OUT_str(item_name); - OUT_str(serial_number); - //strn0cpy(eq->serial_number, emu->serial_number.c_str(), sizeof(eq->serial_number)); + OUT_str(item_unique_id); FINISH_ENCODE(); break; @@ -6174,6 +6172,10 @@ namespace RoF2 ClickTraderNew_Struct out{}; out.action = TraderOn; for (auto i = 0; i < RoF2::invtype::BAZAAR_SIZE; i++) { + if (eq->item_cost[i] == 0) { + continue; + } + BazaarTraderDetails btd{}; btd.unique_id = eq->item_unique_ids[i].item_unique_id; btd.cost = eq->item_cost[i]; @@ -6214,13 +6216,9 @@ namespace RoF2 SETUP_DIRECT_DECODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct); LogTrading("(RoF2) PriceUpdate action [{}]", action); - emu->Action = PriceUpdate; - strn0cpy(emu->serial_number, eq->serial_number, sizeof(emu->serial_number)); - //FIXemu->serial_number = Strings::ToUnsignedBigInt(eq->serial_number, 0); - // if (emu->SerialNumber == 0) { - // LogTrading("(RoF2) Price change with invalid serial number [{}]", eq->serial_number); - // } - emu->NewPrice = eq->new_price; + emu->action = PriceUpdate; + strn0cpy(emu->item_unique_id, eq->item_unique_id, sizeof(emu->item_unique_id)); + emu->new_price = eq->new_price; FINISH_DIRECT_DECODE(); break; @@ -6310,23 +6308,13 @@ namespace RoF2 IN(item_id); IN(trader_id); emu->action = BazaarInspect; - strn0cpy(emu->serial_number, eq->serial_number, sizeof(emu->serial_number)); - //FIX emu->serial_number = Strings::ToUnsignedInt(eq->serial_number, 0); - // if (emu->serial_number == 0) { - // LogTrading( - // "(RoF2) trader_id = [{}] requested a BazaarInspect with an invalid serial number of [{}]", - // eq->trader_id, - // eq->serial_number - // ); - // FINISH_DIRECT_DECODE(); - // return; - // } + strn0cpy(emu->item_unique_id, eq->item_unique_id, sizeof(emu->item_unique_id)); - // LogTrading("(RoF2) BazaarInspect action [{}] item_id [{}] serial_number [{}]", - // action, - // eq->item_id, - // eq->serial_number - // ); + LogTrading("(RoF2) BazaarInspect action [{}] item_id [{}] serial_number [{}]", + action, + eq->item_id, + eq->item_unique_id + ); FINISH_DIRECT_DECODE(); break; } @@ -6363,8 +6351,7 @@ namespace RoF2 IN_str(seller_name); IN_str(item_name); //IN_str(serial_number); - strn0cpy(emu->serial_number, eq->serial_number, sizeof(emu->serial_number)); -//FIX emu->serial_number = eq->serial_number; + strn0cpy(emu->item_unique_id, eq->item_unique_id, sizeof(emu->item_unique_id)); FINISH_DIRECT_DECODE(); break; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 034f97aa7..d544dc1d3 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -3311,7 +3311,7 @@ struct BazaarInspect_Struct { uint32 action; uint32 unknown_004; uint32 trader_id; - char serial_number[17]; + char item_unique_id[17]; char unknown_029[3]; uint32 item_id; uint32 unknown_036; @@ -3594,11 +3594,11 @@ struct BazaarWindowRemoveTrader_Struct { }; struct TraderPriceUpdate_Struct { - uint32 action; - char serial_number[17]; - char unknown_021[3]; - uint32 unknown_024; - uint32 new_price; +/*000*/ uint32 action; +/*004*/ char item_unique_id[17]; +/*021*/ char unknown_021[3]; +/*024*/ uint32 unknown_024; +/*028*/ uint32 new_price; }; struct Trader_ShowItems_Struct { @@ -3625,7 +3625,7 @@ struct TraderBuy_Struct { /*084*/ char seller_name[64]; /*148*/ char unknown_148[32]; /*180*/ char item_name[64]; -/*244*/ char serial_number[17]; +/*244*/ char item_unique_id[17]; /*261*/ char unknown_261[3]; /*264*/ uint32 item_id; /*268*/ uint32 price; @@ -3651,10 +3651,10 @@ struct MoneyUpdate_Struct{ }; struct TraderDelItem_Struct{ - /*000*/ uint32 Unknown000; - /*004*/ uint32 TraderID; - /*008*/ char SerialNumber[17]; - /*024*/ uint32 Unknown012; + /*000*/ uint32 unknown_000; + /*004*/ uint32 trader_id; + /*008*/ char item_unique_id[17]; + /*025*/ uint32 unknown_025; /*028*/ }; diff --git a/common/repositories/base/base_trader_repository.h b/common/repositories/base/base_trader_repository.h index a28ef2db5..b70da0755 100644 --- a/common/repositories/base/base_trader_repository.h +++ b/common/repositories/base/base_trader_repository.h @@ -20,15 +20,15 @@ class BaseTraderRepository { public: struct Trader { uint64_t id; - uint32_t char_id; + uint32_t character_id; uint32_t item_id; - uint32_t aug_slot_1; - uint32_t aug_slot_2; - uint32_t aug_slot_3; - uint32_t aug_slot_4; - uint32_t aug_slot_5; - uint32_t aug_slot_6; - std::string item_sn; + std::string item_unique_id; + uint32_t augment_one; + uint32_t augment_two; + uint32_t augment_three; + uint32_t augment_four; + uint32_t augment_five; + uint32_t augment_six; int32_t item_charges; uint32_t item_cost; uint8_t slot_id; @@ -48,15 +48,15 @@ public: { return { "id", - "char_id", + "character_id", "item_id", - "aug_slot_1", - "aug_slot_2", - "aug_slot_3", - "aug_slot_4", - "aug_slot_5", - "aug_slot_6", - "item_sn", + "item_unique_id", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "item_charges", "item_cost", "slot_id", @@ -72,15 +72,15 @@ public: { return { "id", - "char_id", + "character_id", "item_id", - "aug_slot_1", - "aug_slot_2", - "aug_slot_3", - "aug_slot_4", - "aug_slot_5", - "aug_slot_6", - "item_sn", + "item_unique_id", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "item_charges", "item_cost", "slot_id", @@ -130,15 +130,15 @@ public: Trader e{}; e.id = 0; - e.char_id = 0; + e.character_id = 0; e.item_id = 0; - e.aug_slot_1 = 0; - e.aug_slot_2 = 0; - e.aug_slot_3 = 0; - e.aug_slot_4 = 0; - e.aug_slot_5 = 0; - e.aug_slot_6 = 0; - e.item_sn = ""; + e.item_unique_id = ""; + e.augment_one = 0; + e.augment_two = 0; + e.augment_three = 0; + e.augment_four = 0; + e.augment_five = 0; + e.augment_six = 0; e.item_charges = 0; e.item_cost = 0; e.slot_id = 0; @@ -184,15 +184,15 @@ public: Trader e{}; e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? row[9] : ""; + e.item_unique_id = row[3] ? row[3] : ""; + e.augment_one = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_two = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_three = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_four = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_five = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_six = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; @@ -234,15 +234,15 @@ public: auto columns = Columns(); - v.push_back(columns[1] + " = " + std::to_string(e.char_id)); + v.push_back(columns[1] + " = " + std::to_string(e.character_id)); v.push_back(columns[2] + " = " + std::to_string(e.item_id)); - v.push_back(columns[3] + " = " + std::to_string(e.aug_slot_1)); - v.push_back(columns[4] + " = " + std::to_string(e.aug_slot_2)); - v.push_back(columns[5] + " = " + std::to_string(e.aug_slot_3)); - v.push_back(columns[6] + " = " + std::to_string(e.aug_slot_4)); - v.push_back(columns[7] + " = " + std::to_string(e.aug_slot_5)); - v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_6)); - v.push_back(columns[9] + " = '" + Strings::Escape(e.item_sn) + "'"); + v.push_back(columns[3] + " = '" + Strings::Escape(e.item_unique_id) + "'"); + v.push_back(columns[4] + " = " + std::to_string(e.augment_one)); + v.push_back(columns[5] + " = " + std::to_string(e.augment_two)); + v.push_back(columns[6] + " = " + std::to_string(e.augment_three)); + v.push_back(columns[7] + " = " + std::to_string(e.augment_four)); + v.push_back(columns[8] + " = " + std::to_string(e.augment_five)); + v.push_back(columns[9] + " = " + std::to_string(e.augment_six)); v.push_back(columns[10] + " = " + std::to_string(e.item_charges)); v.push_back(columns[11] + " = " + std::to_string(e.item_cost)); v.push_back(columns[12] + " = " + std::to_string(e.slot_id)); @@ -273,15 +273,15 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.item_id)); - v.push_back(std::to_string(e.aug_slot_1)); - v.push_back(std::to_string(e.aug_slot_2)); - v.push_back(std::to_string(e.aug_slot_3)); - v.push_back(std::to_string(e.aug_slot_4)); - v.push_back(std::to_string(e.aug_slot_5)); - v.push_back(std::to_string(e.aug_slot_6)); - v.push_back("'" + Strings::Escape(e.item_sn) + "'"); + v.push_back("'" + Strings::Escape(e.item_unique_id) + "'"); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.item_cost)); v.push_back(std::to_string(e.slot_id)); @@ -320,15 +320,15 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.item_id)); - v.push_back(std::to_string(e.aug_slot_1)); - v.push_back(std::to_string(e.aug_slot_2)); - v.push_back(std::to_string(e.aug_slot_3)); - v.push_back(std::to_string(e.aug_slot_4)); - v.push_back(std::to_string(e.aug_slot_5)); - v.push_back(std::to_string(e.aug_slot_6)); - v.push_back("'" + Strings::Escape(e.item_sn) + "'"); + v.push_back("'" + Strings::Escape(e.item_unique_id) + "'"); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.item_cost)); v.push_back(std::to_string(e.slot_id)); @@ -371,15 +371,15 @@ public: Trader e{}; e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? row[9] : ""; + e.item_unique_id = row[3] ? row[3] : ""; + e.augment_one = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_two = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_three = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_four = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_five = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_six = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; @@ -413,15 +413,15 @@ public: Trader e{}; e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? row[9] : ""; + e.item_unique_id = row[3] ? row[3] : ""; + e.augment_one = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_two = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_three = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_four = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_five = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_six = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; @@ -505,15 +505,15 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.item_id)); - v.push_back(std::to_string(e.aug_slot_1)); - v.push_back(std::to_string(e.aug_slot_2)); - v.push_back(std::to_string(e.aug_slot_3)); - v.push_back(std::to_string(e.aug_slot_4)); - v.push_back(std::to_string(e.aug_slot_5)); - v.push_back(std::to_string(e.aug_slot_6)); - v.push_back("'" + Strings::Escape(e.item_sn) + "'"); + v.push_back("'" + Strings::Escape(e.item_unique_id) + "'"); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.item_cost)); v.push_back(std::to_string(e.slot_id)); @@ -545,15 +545,15 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.item_id)); - v.push_back(std::to_string(e.aug_slot_1)); - v.push_back(std::to_string(e.aug_slot_2)); - v.push_back(std::to_string(e.aug_slot_3)); - v.push_back(std::to_string(e.aug_slot_4)); - v.push_back(std::to_string(e.aug_slot_5)); - v.push_back(std::to_string(e.aug_slot_6)); - v.push_back("'" + Strings::Escape(e.item_sn) + "'"); + v.push_back("'" + Strings::Escape(e.item_unique_id) + "'"); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.item_cost)); v.push_back(std::to_string(e.slot_id)); diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index b5ac3810d..88cc7910a 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -58,9 +58,9 @@ public: if (RuleB(Bazaar, UseAlternateBazaarSearch)) { results = db.QueryDatabase(fmt::format( - "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " + "SELECT DISTINCT(t.character_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " - "JOIN character_data AS c ON t.char_id = c.id " + "JOIN character_data AS c ON t.character_id = c.id " "WHERE t.char_zone_instance_id = {} " "ORDER BY t.char_zone_instance_id ASC " "LIMIT {}", @@ -69,13 +69,14 @@ public: ); } else { - results = db.QueryDatabase(fmt::format( - "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " - "FROM trader AS t " - "JOIN character_data AS c ON t.char_id = c.id " - "ORDER BY t.char_zone_instance_id ASC " - "LIMIT {}", - max_results) + results = db.QueryDatabase( + fmt::format( + "SELECT DISTINCT(t.character_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " + "FROM trader AS t " + "JOIN character_data AS c ON t.character_id = c.id " + "ORDER BY t.char_zone_instance_id ASC " + "LIMIT {}", + max_results) ); } @@ -101,7 +102,7 @@ public: { WelcomeData_Struct e{}; - auto results = db.QueryDatabase("SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader;"); + auto results = db.QueryDatabase("SELECT COUNT(DISTINCT character_id), count(character_id) FROM trader;"); if (!results.RowCount()) { return e; @@ -113,15 +114,15 @@ public: return e; } - static int UpdateItem(Database &db, uint32 char_id, uint32 new_price, uint32 item_id, uint32 item_charges) + static int UpdateItem(Database &db, uint32 character_id, uint32 new_price, uint32 item_id, uint32 item_charges) { std::vector items{}; if (item_charges == 0) { items = GetWhere( db, fmt::format( - "char_id = '{}' AND item_id = '{}'", - char_id, + "character_id = '{}' AND item_id = '{}'", + character_id, item_id ) ); @@ -130,8 +131,8 @@ public: items = GetWhere( db, fmt::format( - "char_id = '{}' AND item_id = '{}' AND item_charges = '{}'", - char_id, + "character_id = '{}' AND item_id = '{}' AND item_charges = '{}'", + character_id, item_id, item_charges ) @@ -155,8 +156,8 @@ public: Trader item{}; auto query = fmt::format( - "SELECT t.char_id, t.item_id, t.serialnumber, t.charges, t.item_cost, t.slot_id, t.entity_id FROM trader AS t " - "WHERE t.entity_id = {} AND t.item_id = {} AND t.item_cost = {} " + "SELECT t.character_id, t.item_id, t.item_unique.id, t.charges, t.item_cost, t.slot_id, t.entity_id FROM trader AS t " + "WHERE t.entity_id = '{}' AND t.item_id = '{}' AND t.item_cost = '{}' " "LIMIT 1;", trader_id, item_id, @@ -168,41 +169,59 @@ public: return item; } - auto row = results.begin(); - item.char_id = Strings::ToInt(row[0]); - item.item_id = Strings::ToInt(row[1]); - item.item_sn = Strings::ToInt(row[2]); - item.item_charges = Strings::ToInt(row[3]); - item.item_cost = Strings::ToInt(row[4]); - item.slot_id = Strings::ToInt(row[5]); + auto row = results.begin(); + item.character_id = Strings::ToInt(row[0]); + item.item_id = Strings::ToInt(row[1]); + item.item_unique_id = row[2] ? row[2] : ""; + item.item_charges = Strings::ToInt(row[3]); + item.item_cost = Strings::ToInt(row[4]); + item.slot_id = Strings::ToInt(row[5]); return item; } - static int UpdateQuantity(Database &db, uint32 char_id, const std::string &serial_number, int16 quantity) + static int UpdateQuantity(Database &db, const std::string &item_unique_id, int16 quantity) { const auto trader_item = GetWhere( db, - fmt::format("char_id = '{}' AND item_sn = '{}' ", char_id, serial_number) + fmt::format("`item_unique_id` = '{}' ", item_unique_id) ); if (trader_item.empty() || trader_item.size() > 1) { return 0; } - auto m = trader_item[0]; + auto m = trader_item[0]; m.item_charges = quantity; m.listing_date = time(nullptr); return UpdateOne(db, m); } - static Trader GetItemBySerialNumber(Database &db, std::string &serial_number, uint32 trader_id) + static int UpdatePrice(Database &db, const std::string &item_unique_id, uint32 price) + { + const auto trader_item = GetWhere( + db, + fmt::format("`item_unique_id` = '{}' ", item_unique_id) + ); + + if (trader_item.empty() || trader_item.size() > 1) { + return 0; + } + + auto m = trader_item[0]; + m.item_cost = price; + m.listing_date = time(nullptr); + + return ReplaceOne(db, m); + } + + static Trader GetItemBySerialNumber(Database &db, std::string &item_unique_id, uint32 trader_id) { Trader e{}; const auto trader_item = GetWhere( db, - fmt::format("`char_id` = '{}' AND `item_sn` = '{}' LIMIT 1", trader_id, serial_number) + fmt::format("`character_id` = '{}' AND `item_unique_id` = '{}' LIMIT 1", trader_id, item_unique_id) ); if (trader_item.empty()) { @@ -240,21 +259,16 @@ public: return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids))); } - static DistinctTraders_Struct GetTraderByInstanceAndSerialnumber( - Database &db, - uint32 instance_id, - std::string &serial_number - ) + static DistinctTraders_Struct GetTraderByItemUniqueNumber(Database &db, std::string &item_unique_id) { DistinctTraders_Struct trader{}; auto query = fmt::format( - "SELECT t.id, t.char_id, c.name " + "SELECT t.id, t.character_id, c.name " "FROM trader AS t " - "JOIN character_data AS c ON c.id = t.char_id " - "WHERE t.char_zone_id = 151 AND t.char_zone_instance_id = {} AND t.item_sn = {} LIMIT 1", - instance_id, - serial_number + "JOIN character_data AS c ON c.id = t.character_id " + "WHERE t.item_unique_id = '{}' LIMIT 1", + item_unique_id ); auto results = db.QueryDatabase(query); @@ -264,7 +278,6 @@ public: } auto row = results.begin(); - std::string name = row[2]; trader.trader_id = Strings::ToUnsignedInt(row[1]); trader.trader_name = row[2] ? row[2] : ""; @@ -279,8 +292,12 @@ public: std::vector all_entries{}; auto query = fmt::format( - "SELECT trader.*, c.`name` FROM `trader` INNER JOIN character_data AS c ON trader.char_id = c.id " - "WHERE {} ORDER BY trader.char_id ASC", + "SELECT trader.id, trader.character_id, trader.item_id, trader.item_unique_id, trader.augment_one, " + "trader.augment_two, trader.augment_three, trader.augment_four, trader.augment_five, trader.augment_six, " + "trader.item_charges, trader.item_cost, trader.slot_id, trader.char_entity_id, trader.char_zone_id, " + "trader.char_zone_instance_id, trader.active_transaction, c.`name` FROM `trader` " + "INNER JOIN character_data AS c ON trader.character_id = c.id " + "WHERE {} ORDER BY trader.character_id ASC", search_criteria_trader ); @@ -295,15 +312,15 @@ public: BazaarTraderSearch_Struct e{}; e.trader.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.trader.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.trader.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.trader.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.trader.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.trader.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.trader.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.trader.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.trader.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.trader.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.trader.item_sn = row[9] ? row[9] : std::string(""); + e.trader.item_unique_id = row[3] ? row[3] : std::string(""); + e.trader.augment_one = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.trader.augment_two = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.trader.augment_three = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.trader.augment_four = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.trader.augment_five = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.trader.augment_six = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; e.trader.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; e.trader.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.trader.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; diff --git a/zone/client.cpp b/zone/client.cpp index fa00b9609..a27801e2b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -12702,7 +12702,7 @@ uint16 Client::GetSkill(EQ::skills::SkillType skill_id) const return 0; } -void Client::RemoveItemBySerialNumber(const std::string &item_unique_id, uint32 quantity) +bool Client::RemoveItemByItemUniqueId(const std::string &item_unique_id, uint32 quantity) { EQ::ItemInstance *item = nullptr; uint32 removed_count = 0; @@ -12717,18 +12717,24 @@ void Client::RemoveItemBySerialNumber(const std::string &item_unique_id, uint32 if (item && item->GetUniqueID().compare(item_unique_id) == 0) { uint32 charges = item->IsStackable() ? item->GetCharges() : 0; uint32 stack_size = std::max(charges, static_cast(1)); - if ((removed_count + stack_size) <= quantity) { + if (removed_count + stack_size <= quantity) { removed_count += stack_size; - DeleteItemInInventory(slot_id, charges, true); + if (DeleteItemInInventory(slot_id, charges, true)) { + return true; + } } else { - uint32 amount_left = (quantity - removed_count); + uint32 amount_left = quantity - removed_count; if (amount_left > 0 && stack_size >= amount_left) { removed_count += amount_left; - DeleteItemInInventory(slot_id, amount_left, true); + if (DeleteItemInInventory(slot_id, amount_left, true)) { + return true; + } } } } } + + return false; } void Client::SendTopLevelInventory() diff --git a/zone/client.h b/zone/client.h index 3579e05a7..5cc62aab4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -314,7 +314,8 @@ public: void Trader_CustomerBrowsing(Client *Customer); void TraderEndTrader(); - void TraderPriceUpdate(const EQApplicationPacket *app); + //void TraderPriceUpdate(const EQApplicationPacket *app); + void TraderUpdateItem(const EQApplicationPacket *app); void SendBazaarDone(uint32 trader_id); void SendBulkBazaarTraders(); void SendBulkBazaarBuyers(); @@ -375,11 +376,12 @@ public: uint32 FindTraderItemSerialNumber(int32 ItemID); EQ::ItemInstance* FindTraderItemBySerialNumber(std::string &serial_number); EQ::ItemInstance* FindTraderItemByUniqueID(std::string &unique_id); - void FindAndNukeTraderItem(std::string &serial_number, int16 quantity, Client* customer, uint16 trader_slot); + EQ::ItemInstance* FindTraderItemByUniqueID(const char* unique_id); + void FindAndNukeTraderItem(std::string &item_unique_id, int16 quantity, Client* customer, uint16 trader_slot); void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 trader_slot, const std::string &serial_number, int32 item_id = 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 BuyTraderItem(const EQApplicationPacket* app); void BuyTraderItemOutsideBazaar(TraderBuy_Struct* tbs, const EQApplicationPacket* app); void FinishTrade( Mob *with, @@ -416,9 +418,15 @@ public: void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action); void SendBecomeTrader(BazaarTraderBarterActions action, uint32 trader_id); - bool IsThereACustomer() const { return customer_id ? true : false; } + bool IsThereACustomer() const { return customer_id ? true : false; } uint32 GetCustomerID() { return customer_id; } - void SetCustomerID(uint32 id) { customer_id = id; } + void SetCustomerID(uint32 id) { customer_id = id; } + void ClearTraderMerchantList() { m_trader_merchant_list.clear(); } + void AddDataToMerchantList(int16 slot_id, int32 quantity, const std::string &item_unique_id); + std::tuple GetDataFromMerchantListByMerchantSlotId(int16 slot_id); + int16 GetSlotFromMerchantListByItemUniqueId(const std::string &unique_id); + std::pair> GetDataFromMerchantListByItemUniqueId(const std::string &unique_id); + std::map>* GetTraderMerchantList() { return &m_trader_merchant_list; } void SetBuyerID(uint32 id) { m_buyer_id = id; } uint32 GetBuyerID() { return m_buyer_id; } @@ -587,7 +595,7 @@ public: void DisableAreaRegens(); void ServerFilter(SetServerFilter_Struct* filter); - void BulkSendTraderInventory(uint32 char_id); + void BulkSendTraderInventory(uint32 character_id); void SendSingleTraderItem(uint32 char_id, const std::string &serial_number); void BulkSendMerchantInventory(int merchant_id, int npcid); @@ -1145,13 +1153,13 @@ public: bool FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector items); bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false); void SendCursorBuffer(); - void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); + bool DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); uint32 CountItem(uint32 item_id); void ResetItemCooldown(uint32 item_id); void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1); uint32 GetItemCooldown(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity = 1); - void RemoveItemBySerialNumber(const std::string &item_unique_id, uint32 quantity = 1); + bool RemoveItemByItemUniqueId(const std::string &item_unique_id, uint32 quantity = 1); bool SwapItem(MoveItem_Struct* move_in); void SwapItemResync(MoveItem_Struct* move_slots); void PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, LootItem** bag_item_data = 0); @@ -2104,6 +2112,7 @@ private: uint8 mercSlot; // selected merc slot time_t m_trader_transaction_date; uint32 m_trader_count{}; + std::map> m_trader_merchant_list{}; uint32 m_buyer_id; uint32 m_barter_time; int32 m_parcel_platinum; diff --git a/zone/client_evolving_items.cpp b/zone/client_evolving_items.cpp index 44184e229..05f3d4234 100644 --- a/zone/client_evolving_items.cpp +++ b/zone/client_evolving_items.cpp @@ -386,7 +386,7 @@ bool Client::DoEvolveCheckProgression(EQ::ItemInstance &inst) PlayerEvent::EvolveItem e{}; - RemoveItemBySerialNumber(inst.GetUniqueID()); + RemoveItemByItemUniqueId(inst.GetUniqueID()); EvolvingItemsManager::Instance()->LoadPlayerEvent(inst, e); e.status = "Evolved Item due to obtaining progression - Old Evolve Item removed from inventory."; RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); @@ -506,7 +506,7 @@ void Client::DoEvolveTransferXP(const EQApplicationPacket *app) PlayerEvent::EvolveItem e{}; - RemoveItemBySerialNumber(inst_from->GetUniqueID()); + RemoveItemByItemUniqueId(inst_from->GetUniqueID()); EvolvingItemsManager::Instance()->LoadPlayerEvent(*inst_from, e); e.status = "Transfer XP - Original FROM Evolve Item removed from inventory."; RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); @@ -516,7 +516,7 @@ void Client::DoEvolveTransferXP(const EQApplicationPacket *app) e.status = "Transfer XP - Updated FROM item placed in inventory."; RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); - RemoveItemBySerialNumber(inst_to->GetUniqueID()); + RemoveItemByItemUniqueId(inst_to->GetUniqueID()); EvolvingItemsManager::Instance()->LoadPlayerEvent(*inst_to, e); e.status = "Transfer XP - Original TO Evolve Item removed from inventory."; RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 5b8fb836d..00082ce29 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -15434,10 +15434,10 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) TraderStartTrader(app); break; } - case PriceUpdate: - case ItemMove: { - LogTrading("Trader Price Update"); - TraderPriceUpdate(app); + case ItemMove: + case PriceUpdate:{ + LogTrading("Trader item updated - removed, added or price change"); + TraderUpdateItem(app); break; } case EndTransaction: { @@ -15456,7 +15456,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) break; } default: { - LogError("Unknown size for OP_Trader: [{}]\n", app->size); + LogTrading("Unknown size for OP_Trader: [{}]\n", app->size); } } } @@ -15467,29 +15467,12 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) // // Client has elected to buy an item from a Trader // - auto in = (TraderBuy_Struct *) app->pBuffer; - if (RuleB(Bazaar, UseAlternateBazaarSearch) && in->trader_id >= TraderRepository::TRADER_CONVERT_ID) { - auto sn = std::string(in->serial_number); - auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( - database, - in->trader_id - TraderRepository::TRADER_CONVERT_ID, - sn - ); - - if (!trader.trader_id) { - LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", - in->trader_id - TraderRepository::TRADER_CONVERT_ID, - in->serial_number - ); - return; - } - - in->trader_id = trader.trader_id; - strn0cpy(in->seller_name, trader.trader_name.c_str(), sizeof(in->seller_name)); - } - - auto trader = entity_list.GetClientByID(in->trader_id); + auto in = (TraderBuy_Struct *) app->pBuffer; + auto item_unique_id = std::string(in->item_unique_id); + auto trader_details = TraderRepository::GetTraderByItemUniqueNumber(database, item_unique_id); + auto trader = entity_list.GetClientByID(in->trader_id); + strn0cpy(in->seller_name, trader_details.trader_name.c_str(), sizeof(in->seller_name)); switch (in->method) { case BazaarByVendor: { @@ -15499,9 +15482,9 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) in->trader_id, in->item_id, in->quantity, - in->serial_number + in->item_unique_id ); - BuyTraderItem(in, trader, app); + BuyTraderItem(app); } break; } @@ -15525,7 +15508,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) in->trader_id, in->item_id, in->quantity, - in->serial_number + in->item_unique_id ); BuyTraderItemOutsideBazaar(in, app); break; @@ -15550,7 +15533,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) in->trader_id, in->item_id, in->quantity, - in->serial_number + in->item_unique_id ); Message( Chat::Yellow, @@ -15561,6 +15544,9 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) TradeRequestFailed(app); break; } + default: { + + } } } @@ -15651,17 +15637,18 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) switch (in->Code) { case ClickTrader: { LogTrading("Handle_OP_TraderShop case ClickTrader [{}]", in->Code); - auto outapp = - std::make_unique(OP_TraderShop, static_cast(sizeof(TraderClick_Struct)) + auto outapp = std::make_unique( + OP_TraderShop, + static_cast(sizeof(TraderClick_Struct)) ); auto data = (TraderClick_Struct *) outapp->pBuffer; - auto trader_client = entity_list.GetClientByID(in->TraderID); + auto trader = entity_list.GetClientByID(in->TraderID); - if (trader_client) { - data->Approval = trader_client->WithCustomer(GetID()); + if (trader) { + data->Approval = trader->WithCustomer(GetID()); LogTrading("Client::Handle_OP_TraderShop: Shop Request ([{}]) to ([{}]) with Approval: [{}]", GetCleanName(), - trader_client->GetCleanName(), + trader->GetCleanName(), data->Approval ); } @@ -15669,6 +15656,9 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) LogTrading("Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" " returned a nullptr pointer" ); + auto outapp = new EQApplicationPacket(OP_ShopEndConfirm); + QueuePacket(outapp); + safe_delete(outapp); return; } @@ -15678,8 +15668,9 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) QueuePacket(outapp.get()); if (data->Approval) { - BulkSendTraderInventory(trader_client->CharacterID()); - trader_client->Trader_CustomerBrowsing(this); + ClearTraderMerchantList(); + BulkSendTraderInventory(trader->CharacterID()); + trader->Trader_CustomerBrowsing(this); SetTraderID(in->TraderID); LogTrading("Client::Handle_OP_TraderShop: Trader Inventory Sent to [{}] from [{}]", GetID(), diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 3c10ee909..340ede084 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -958,7 +958,7 @@ void Client::SendCursorBuffer() } // Remove item from inventory -void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_update, bool update_db) { +bool Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_update, bool update_db) { #if (EQDEBUG >= 5) LogDebug("DeleteItemInInventory([{}], [{}], [{}])", slot_id, quantity, (client_update) ? "true":"false"); #endif @@ -977,7 +977,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up QueuePacket(outapp); safe_delete(outapp); } - return; + return false; } uint64 evolve_id = m_inv[slot_id]->GetEvolveUniqueID(); @@ -1031,6 +1031,8 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up safe_delete(outapp); } } + + return true; } bool Client::PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update) diff --git a/zone/parcels.cpp b/zone/parcels.cpp index d6bb7a494..fef298f87 100644 --- a/zone/parcels.cpp +++ b/zone/parcels.cpp @@ -458,7 +458,7 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) CharacterParcelsContainersRepository::InsertMany(database, all_entries); } - RemoveItemBySerialNumber(inst->GetUniqueID(), parcel_out.quantity == 0 ? 1 : parcel_out.quantity); + RemoveItemByItemUniqueId(inst->GetUniqueID(), parcel_out.quantity == 0 ? 1 : parcel_out.quantity); std::unique_ptr outapp(new EQApplicationPacket(OP_ShopSendParcel)); QueuePacket(outapp.get()); diff --git a/zone/string_ids.h b/zone/string_ids.h index 5e929db02..51e981476 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -309,6 +309,7 @@ #define PLAYER_CHARMED 1461 //You lose control of yourself! #define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished. #define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction. +#define HOW_CAN_YOU_BUY_MORE 1571 //%1 tells you, 'Your inventory appears full! How can you buy more?' #define DUPE_LORE_MERCHANT 1573 //%1 tells you, 'You already have the lore item, %2, on your person, on your shroud, in the bank, in a real estate, or as an augment in another item. You cannot have more than one of a particular lore item at a time.' #define QUEUED_TELL 2458 //[queued] #define QUEUE_TELL_FULL 2459 //[zoing and queue is full] @@ -466,6 +467,8 @@ #define LDON_NO_LOCKPICK 7564 //You must have a lock pick in your inventory to do this. #define LDON_WAS_NOT_LOCKED 7565 //%1 was not locked. #define LDON_WAS_NOT_TRAPPED 7566 //%1 was not trapped +#define DUPLICATE_LORE 7623 //Transaction failed: Duplicate Lore Item! +#define INSUFFICIENT_FUNDS 7632 //Transaction failed: Insufficient funds! #define GAIN_SINGLE_AA_SINGLE_AA 8019 //You have gained an ability point! You now have %1 ability point. #define GAIN_SINGLE_AA_MULTI_AA 8020 //You have gained an ability point! You now have %1 ability points. #define GAIN_MULTI_AA_MULTI_AA 8021 //You have gained %1 ability point(s)! You now have %2 ability point(s). diff --git a/zone/trading.cpp b/zone/trading.cpp index 55a8a0072..2aecdaafa 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -759,7 +759,7 @@ void Client::TraderShowItems() std::stringstream ss{}; cereal::BinaryOutputArchive ar(ss); - auto trader_items = TraderRepository::GetWhere(database, fmt::format("`char_id` = '{}'", CharacterID())); + auto trader_items = TraderRepository::GetWhere(database, fmt::format("`character_id` = {}", CharacterID())); if (trader_items.empty()) { return; } @@ -769,9 +769,9 @@ void Client::TraderShowItems() for (auto const &t: trader_items) { TraderItems_Struct items{}; - items.serial_number = t.item_sn; - items.item_id = t.item_id; - items.item_cost = t.item_cost; + items.item_unique_id = t.item_unique_id; + items.item_id = t.item_id; + items.item_cost = t.item_cost; tcm.items.push_back(items); } @@ -861,23 +861,23 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) trader_item.id = 0; trader_item.char_entity_id = GetID(); - trader_item.char_id = CharacterID(); + trader_item.character_id = CharacterID(); trader_item.char_zone_id = GetZoneID(); trader_item.char_zone_instance_id = GetInstanceID(); - trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); + trader_item.item_charges = inst->GetCharges(); trader_item.item_cost = i.cost; trader_item.item_id = inst->GetID(); - trader_item.item_sn = i.unique_id; + trader_item.item_unique_id = i.unique_id; trader_item.slot_id = slot_id; trader_item.listing_date = time(nullptr); if (inst->IsAugmented()) { - auto augs = inst->GetAugmentIDs(); - trader_item.aug_slot_1 = augs.at(0); - trader_item.aug_slot_2 = augs.at(1); - trader_item.aug_slot_3 = augs.at(2); - trader_item.aug_slot_4 = augs.at(3); - trader_item.aug_slot_5 = augs.at(4); - trader_item.aug_slot_6 = augs.at(5); + auto augs = inst->GetAugmentIDs(); + trader_item.augment_one = augs.at(0); + trader_item.augment_two = augs.at(1); + trader_item.augment_three = augs.at(2); + trader_item.augment_four = augs.at(3); + trader_item.augment_five = augs.at(4); + trader_item.augment_six = augs.at(5); } trader_items.emplace_back(trader_item); @@ -890,7 +890,7 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) return; } - TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}';", CharacterID())); + TraderRepository::DeleteWhere(database, fmt::format("`character_id` = {};", CharacterID())); TraderRepository::ReplaceMany(database, trader_items); safe_delete(inv); @@ -920,7 +920,7 @@ void Client::TraderEndTrader() } } - TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}'", CharacterID())); + TraderRepository::DeleteWhere(database, fmt::format("`character_id` = {}", CharacterID())); SendBecomeTraderToWorld(this, TraderOff); SendTraderMode(TraderOff); @@ -945,12 +945,12 @@ void Client::SendTraderItem(uint32 ItemID, uint16 Quantity, TraderRepository::Tr database.CreateItem( item, Quantity, - t.aug_slot_1, - t.aug_slot_2, - t.aug_slot_3, - t.aug_slot_4, - t.aug_slot_5, - t.aug_slot_6 + t.augment_one, + t.augment_two, + t.augment_three, + t.augment_four, + t.augment_five, + t.augment_six ) ); @@ -969,58 +969,50 @@ void Client::SendTraderItem(uint32 ItemID, uint16 Quantity, TraderRepository::Tr } } -void Client::SendSingleTraderItem(uint32 char_id, const std::string &serial_number) +void Client::SendSingleTraderItem(uint32 character_id, const std::string &serial_number) { - auto inst = database.LoadSingleTraderItem(char_id, serial_number); + auto inst = database.LoadSingleTraderItem(character_id, serial_number); if (inst) { SendItemPacket(EQ::invslot::slotCursor, inst.get(), ItemPacketMerchant); // MainCursor? } } -void Client::BulkSendTraderInventory(uint32 char_id) +void Client::BulkSendTraderInventory(uint32 character_id) { const EQ::ItemData *item; - auto trader_items = TraderRepository::GetWhere(database, fmt::format("`char_id` = '{}'", char_id)); + auto trader_items = TraderRepository::GetWhere(database, fmt::format("`character_id` = {}", character_id)); uint32 item_limit = trader_items.size() >= GetInv().GetLookup()->InventoryTypeSize.Bazaar ? GetInv().GetLookup()->InventoryTypeSize.Bazaar : trader_items.size(); - for (uint32 i = 0; i < item_limit; i++) { - if ((trader_items.at(i).item_id == 0) || (trader_items.at(i).item_cost == 0)) { + for (int16 i = 0; i < item_limit; i++) { + if (trader_items.at(i).item_id == 0 || trader_items.at(i).item_cost == 0) { continue; } - else { - item = database.GetItem(trader_items.at(i).item_id); - } + + item = database.GetItem(trader_items.at(i).item_id); if (item && (item->NoDrop != 0)) { std::unique_ptr inst( database.CreateItem( trader_items.at(i).item_id, trader_items.at(i).item_charges, - trader_items.at(i).aug_slot_1, - trader_items.at(i).aug_slot_2, - trader_items.at(i).aug_slot_3, - trader_items.at(i).aug_slot_4, - trader_items.at(i).aug_slot_5, - trader_items.at(i).aug_slot_6 + trader_items.at(i).augment_one, + trader_items.at(i).augment_two, + trader_items.at(i).augment_three, + trader_items.at(i).augment_four, + trader_items.at(i).augment_five, + trader_items.at(i).augment_six ) ); if (inst) { - inst->SetUniqueID(trader_items.at(i).item_sn); - if (trader_items.at(i).item_charges > 0) { - inst->SetCharges(trader_items.at(i).item_charges); - } - - if (inst->IsStackable()) { - inst->SetMerchantCount(trader_items.at(i).item_charges); - //inst->SetMerchantSlot(trader_items.at(i).item_sn); - } - + inst->SetUniqueID(trader_items.at(i).item_unique_id); + inst->SetMerchantCount(inst->IsStackable() ? inst->GetCharges() : 1); + inst->SetMerchantSlot(i + 1); inst->SetPrice(trader_items.at(i).item_cost); - SendItemPacket(EQ::invslot::slotCursor, inst.get(), ItemPacketMerchant); -// safe_delete(inst); + AddDataToMerchantList(i + 1, inst->GetMerchantCount(), inst->GetUniqueID()); + SendItemPacket(i + 1, inst.get(), ItemPacketMerchant); } else LogTrading("Client::BulkSendTraderInventory nullptr inst pointer"); @@ -1098,8 +1090,32 @@ EQ::ItemInstance *Client::FindTraderItemByUniqueID(std::string &unique_id) } } - LogTrading("Couldn't find item! Serial No. was [{}]", unique_id); + LogTrading("Couldn't find item! item_unique_id was [{}]", unique_id); + return nullptr; +} +EQ::ItemInstance *Client::FindTraderItemByUniqueID(const char* unique_id) +{ + EQ::ItemInstance *item = nullptr; + int16 slot_id = 0; + + for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { + item = GetInv().GetItem(i); + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) { + for (int16 x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++) { + // we already have the parent bag and a contents iterator..why not just iterate the bag!?? + slot_id = EQ::InventoryProfile::CalcSlotId(i, x); + item = GetInv().GetItem(slot_id); + if (item) { + if (item->GetUniqueID().compare(unique_id) == 0) { + return item; + } + } + } + } + } + + LogTrading("Couldn't find item! item_unique_id was [{}]", unique_id); return nullptr; } @@ -1188,6 +1204,7 @@ void Client::NukeTraderItem( tdis->unknown_000 = 0; tdis->trader_id = customer->GetID(); tdis->item_id = Strings::ToUnsignedBigInt(serial_number); + strn0cpy(tdis->item_unique_id, serial_number.c_str(), sizeof(tdis->item_unique_id)); tdis->unknown_012 = 0; customer->QueuePacket(outapp); safe_delete(outapp); @@ -1224,17 +1241,18 @@ void Client::NukeTraderItem( safe_delete(outapp2); } -void Client::FindAndNukeTraderItem(std::string &serial_number, int16 quantity, Client *customer, uint16 trader_slot) +void Client::FindAndNukeTraderItem(std::string &item_unique_id, int16 quantity, Client *customer, uint16 trader_slot) { - const EQ::ItemInstance *item = nullptr; - bool stackable = false; - int16 charges = 0; - uint16 slot_id = FindTraderItem(serial_number, quantity); + const EQ::ItemInstance *item = nullptr; + bool stackable = false; + int16 charges = 0; + uint16 slot_id = FindTraderItem(item_unique_id, quantity); + if (slot_id > 0) { item = GetInv().GetItem(slot_id); if (!item) { - LogTrading("Could not find Item: [{}] on Trader: [{}]", serial_number, quantity, GetName()); + LogTrading("Could not find Item: [{}] on Trader: [{}]", item_unique_id, quantity, GetName()); return; } @@ -1252,7 +1270,7 @@ void Client::FindAndNukeTraderItem(std::string &serial_number, int16 quantity, C if (charges <= quantity || (charges <= 0 && quantity == 1) || !stackable) { DeleteItemInInventory(slot_id, quantity); - auto trader_items = TraderRepository::GetWhere(database, fmt::format("`char_id` = '{}'", CharacterID())); + auto trader_items = TraderRepository::GetWhere(database, fmt::format("`character_id` = {}", CharacterID())); uint32 item_limit = trader_items.size() >= GetInv().GetLookup()->InventoryTypeSize.Bazaar ? GetInv().GetLookup()->InventoryTypeSize.Bazaar : trader_items.size(); @@ -1261,7 +1279,7 @@ void Client::FindAndNukeTraderItem(std::string &serial_number, int16 quantity, C std::vector delete_queue{}; for (int i = 0; i < item_limit; i++) { - if (test_slot && trader_items.at(i).item_sn.compare(serial_number) == 0) { + if (test_slot && trader_items.at(i).item_unique_id.compare(item_unique_id) == 0) { delete_queue.push_back(trader_items.at(i)); NukeTraderItem( slot_id, @@ -1269,7 +1287,7 @@ void Client::FindAndNukeTraderItem(std::string &serial_number, int16 quantity, C quantity, customer, trader_slot, - trader_items.at(i).item_sn, + trader_items.at(i).item_unique_id, trader_items.at(i).item_id ); test_slot = false; @@ -1287,13 +1305,12 @@ void Client::FindAndNukeTraderItem(std::string &serial_number, int16 quantity, C return; } else { - TraderRepository::UpdateQuantity(database, CharacterID(), item->GetUniqueID(), charges - quantity); NukeTraderItem(slot_id, charges, quantity, customer, trader_slot, item->GetUniqueID(), item->GetID()); return; } } LogTrading("Could NOT find a match for Item: [{}] with a quantity of: [{}] on Trader: [{}]\n", - serial_number, + item_unique_id, quantity, GetName() ); @@ -1354,109 +1371,62 @@ 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(const EQApplicationPacket *app) { - if (!Trader) { - return; - } + auto in = reinterpret_cast(app->pBuffer); + auto trader = entity_list.GetClientByID(in->trader_id); - if (!Trader->IsTrader()) { + if (!trader || !trader->IsTrader()) { + Message(Chat::Red, "The trader could not be found."); TradeRequestFailed(app); return; } - auto outapp = std::make_unique(OP_Trader, static_cast(sizeof(TraderBuy_Struct))); - auto outtbs = (TraderBuy_Struct *) outapp->pBuffer; - outtbs->item_id = tbs->item_id; + auto trader_packet = std::make_unique(OP_Trader, static_cast(sizeof(TraderBuy_Struct))); + auto data = reinterpret_cast(trader_packet->pBuffer); - const EQ::ItemInstance *buy_item = nullptr; - uint32 item_id = 0; - - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { - tbs->item_id = Strings::ToUnsignedBigInt(tbs->serial_number); - } - - auto sn = std::string(tbs->serial_number); - buy_item = Trader->FindTraderItemBySerialNumber(sn); - - if (!buy_item) { - LogTrading("Unable to find item id [{}] item_sn [{}] on trader", tbs->item_id, tbs->serial_number); + auto buy_inst = trader->FindTraderItemByUniqueID(in->item_unique_id); + if (!buy_inst) { + LogTrading("Unable to find item id [{}] item_sn [{}] on trader", in->item_id, in->item_unique_id); + Message(Chat::Red, "The trader no longer has the item for sale. Please refresh the merchant window."); TradeRequestFailed(app); return; } + uint32 quantity = in->quantity; LogTrading( - "Name: [{}] IsStackable: [{}] Requested Quantity: [{}] Charges on Item [{}]", - buy_item->GetItem()->Name, - buy_item->IsStackable(), - tbs->quantity, - buy_item->GetCharges() + "Name: [{}] IsStackable: [{}] Requested Quantity: [{}]", + buy_inst->GetItem()->Name, + buy_inst->IsStackable(), + quantity ); - // If the item is not stackable, then we can only be buying one of them. - if (!buy_item->IsStackable()) { - outtbs->quantity = 1; // normally you can't send more than 1 here - } - else { - // Stackable items, arrows, diamonds, etc - int32 item_charges = buy_item->GetCharges(); - // ItemCharges for stackables should not be <= 0 - if (item_charges <= 0) { - outtbs->quantity = 1; - // If the purchaser requested more than is in the stack, just sell them how many are actually in the stack. - } - else if (static_cast(item_charges) < tbs->quantity) { - outtbs->quantity = item_charges; - } - else { - outtbs->quantity = tbs->quantity; - } - } - LogTrading("Actual quantity that will be traded is [{}]", outtbs->quantity); - - if ((tbs->price * outtbs->quantity) <= 0) { + if (in->price * quantity <= 0) { Message(Chat::Red, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); - Trader->Message( - Chat::Red, - "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1" - ); + trader->Message(Chat::Red, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); LogError( "Bazaar: Zero price transaction between [{}] and [{}] aborted. Item: [{}] Charges: " "[{}] Qty [{}] Price: [{}]", - GetName(), - Trader->GetName(), - buy_item->GetItem()->Name, - buy_item->GetCharges(), - tbs->quantity, - tbs->price + GetCleanName(), + trader->GetCleanName(), + buy_inst->GetItem()->Name, + buy_inst->GetCharges(), + quantity, + in->price ); TradeRequestFailed(app); return; } - uint64 total_transaction_value = static_cast(tbs->price) * static_cast(outtbs->quantity); - + uint64 total_transaction_value = static_cast(in->price) * static_cast(quantity); if (total_transaction_value > MAX_TRANSACTION_VALUE) { - Message( - Chat::Red, - "That would exceed the single transaction limit of %u platinum.", - MAX_TRANSACTION_VALUE / 1000 - ); + Message(Chat::Red,"That would exceed the single transaction limit of %u platinum.", MAX_TRANSACTION_VALUE / 1000); TradeRequestFailed(app); return; } // This cannot overflow assuming MAX_TRANSACTION_VALUE, checked above, is the default of 2000000000 - uint32 total_cost = tbs->price * outtbs->quantity; - - if (Trader->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - // RoF+ uses individual item price where older clients use total price - outtbs->price = tbs->price; - } - else { - outtbs->price = total_cost; - } - + uint32 total_cost = in->price * quantity; if (!TakeMoneyFromPP(total_cost)) { RecordPlayerEventLog( PlayerEvent::POSSIBLE_HACK, @@ -1464,6 +1434,7 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic .message = "Attempted to buy something in bazaar but did not have enough money." } ); + MessageString(Chat::Red, INSUFFICIENT_FUNDS); TradeRequestFailed(app); return; } @@ -1471,103 +1442,121 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic LogTrading("Customer Paid: [{}] in Copper", total_cost); uint32 platinum = total_cost / 1000; - total_cost -= (platinum * 1000); + total_cost -= platinum * 1000; uint32 gold = total_cost / 100; - total_cost -= (gold * 100); + total_cost -= gold * 100; uint32 silver = total_cost / 10; - total_cost -= (silver * 10); + total_cost -= silver * 10; uint32 copper = total_cost; - Trader->AddMoneyToPP(copper, silver, gold, platinum, true); + LogTrading("Trader Received: [{}] Platinum, [{}] Gold, [{}] Silver, [{}] Copper", platinum, gold, silver, copper); + ReturnTraderReq(app, quantity, buy_inst->GetID()); - if (buy_item && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::TRADER_PURCHASE)) { + if (CheckLoreConflict(buy_inst->GetItem())) { + MessageString(Chat::Red, DUPLICATE_LORE); + TradeRequestFailed(app); + return; + } + + if (quantity != 1) { + buy_inst->SetCharges(quantity); + } + + if (!trader->RemoveItemByItemUniqueId(buy_inst->GetUniqueID(), quantity)) { + Message(Chat::Red, "The Trader no longer has the item. Please refresh the merchant window."); + TradeRequestFailed(app); + return; + } + + trader->AddMoneyToPP(copper, silver, gold, platinum, true); + + if (!AutoPutLootInInventory(*buy_inst, false, true)) { + MessageString(Chat::Red, HOW_CAN_YOU_BUY_MORE, trader->GetCleanName()); + TradeRequestFailed(app); + return; + } + + auto [slot_id, merchant_data] = GetDataFromMerchantListByItemUniqueId(buy_inst->GetUniqueID()); + auto [merchant_quantity, item_unique_id] = merchant_data; + + data->action = BazaarBuyItem; + data->price = in->price; + data->quantity = quantity; + data->trader_id = trader->GetID(); + strn0cpy(data->seller_name, trader->GetCleanName(), sizeof(data->seller_name)); + strn0cpy(data->buyer_name, GetCleanName(), sizeof(data->buyer_name)); + strn0cpy(data->item_name, buy_inst->GetItem()->Name, sizeof(data->item_name)); + strn0cpy(data->item_unique_id, buy_inst->GetUniqueID().data(), sizeof(data->item_unique_id)); + trader->QueuePacket(trader_packet.get()); + + if (merchant_quantity > quantity) { + buy_inst->SetMerchantCount(merchant_quantity - quantity); + buy_inst->SetMerchantSlot(slot_id); + buy_inst->SetPrice(in->price); + auto list = GetTraderMerchantList(); + std::get<0>(list->at(slot_id)) -= quantity; + SendItemPacket(slot_id, buy_inst, ItemPacketMerchant); + TraderRepository::UpdateQuantity(database, item_unique_id, merchant_quantity - quantity); + } + else { + auto client_packet = new EQApplicationPacket(OP_ShopDelItem, static_cast(sizeof(Merchant_DelItem_Struct))); + auto client_data = reinterpret_cast(client_packet->pBuffer); + + client_data->npcid = trader->GetID(); + client_data->playerid = GetID(); + client_data->itemslot = slot_id; + + QueuePacket(client_packet); + safe_delete(client_packet); + + auto list = GetTraderMerchantList(); + list->erase(slot_id); + TraderRepository::DeleteWhere(database, fmt::format("`item_unique_id` = '{}'", item_unique_id)); + } + + if (buy_inst && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::TRADER_PURCHASE)) { auto e = PlayerEvent::TraderPurchaseEvent{ - .item_id = buy_item->GetID(), - .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), - .item_name = buy_item->GetItem()->Name, - .trader_id = Trader->CharacterID(), - .trader_name = Trader->GetCleanName(), - .price = tbs->price, - .quantity = outtbs->quantity, - .charges = buy_item->GetCharges(), - .total_cost = (tbs->price * outtbs->quantity), + .item_id = buy_inst->GetID(), + .augment_1_id = buy_inst->GetAugmentItemID(0), + .augment_2_id = buy_inst->GetAugmentItemID(1), + .augment_3_id = buy_inst->GetAugmentItemID(2), + .augment_4_id = buy_inst->GetAugmentItemID(3), + .augment_5_id = buy_inst->GetAugmentItemID(4), + .augment_6_id = buy_inst->GetAugmentItemID(5), + .item_name = buy_inst->GetItem()->Name, + .trader_id = trader->CharacterID(), + .trader_name = trader->GetCleanName(), + .price = in->price, + .quantity = quantity, + .charges = buy_inst->GetCharges(), + .total_cost = total_cost, .player_money_balance = GetCarriedMoney(), }; RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e); } - if (buy_item && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::TRADER_SELL)) { + if (buy_inst && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::TRADER_SELL)) { auto e = PlayerEvent::TraderSellEvent{ - .item_id = buy_item->GetID(), - .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), - .item_name = buy_item->GetItem()->Name, + .item_id = buy_inst->GetID(), + .augment_1_id = buy_inst->GetAugmentItemID(0), + .augment_2_id = buy_inst->GetAugmentItemID(1), + .augment_3_id = buy_inst->GetAugmentItemID(2), + .augment_4_id = buy_inst->GetAugmentItemID(3), + .augment_5_id = buy_inst->GetAugmentItemID(4), + .augment_6_id = buy_inst->GetAugmentItemID(5), + .item_name = buy_inst->GetItem()->Name, .buyer_id = CharacterID(), .buyer_name = GetCleanName(), - .price = tbs->price, - .quantity = outtbs->quantity, - .charges = buy_item->GetCharges(), - .total_cost = (tbs->price * outtbs->quantity), - .player_money_balance = Trader->GetCarriedMoney(), + .price = in->price, + .quantity = quantity, + .charges = buy_inst->GetCharges(), + .total_cost = total_cost, + .player_money_balance = trader->GetCarriedMoney(), }; - RecordPlayerEventLogWithClient(Trader, PlayerEvent::TRADER_SELL, e); + RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADER_SELL, e); } - - LogTrading("Trader Received: [{}] Platinum, [{}] Gold, [{}] Silver, [{}] Copper", platinum, gold, silver, copper); - ReturnTraderReq(app, outtbs->quantity, item_id); - - outtbs->trader_id = GetID(); - outtbs->action = BazaarBuyItem; - strn0cpy(outtbs->seller_name, Trader->GetCleanName(), sizeof(outtbs->seller_name)); - strn0cpy(outtbs->buyer_name, GetCleanName(), sizeof(outtbs->buyer_name)); - strn0cpy(outtbs->item_name, buy_item->GetItem()->Name, sizeof(outtbs->item_name)); - strn0cpy( - outtbs->serial_number, - buy_item->GetUniqueID().data(), - sizeof(outtbs->serial_number) - ); - - TraderRepository::Trader t{}; - t.item_charges = buy_item->IsStackable() ? outtbs->quantity : buy_item->GetCharges(); - t.item_id = buy_item->GetItem()->ID; - t.aug_slot_1 = buy_item->GetAugmentItemID(0); - t.aug_slot_2 = buy_item->GetAugmentItemID(1); - t.aug_slot_3 = buy_item->GetAugmentItemID(2); - t.aug_slot_4 = buy_item->GetAugmentItemID(3); - t.aug_slot_5 = buy_item->GetAugmentItemID(4); - t.aug_slot_6 = buy_item->GetAugmentItemID(5); - t.char_id = CharacterID(); - t.slot_id = FindNextFreeParcelSlot(CharacterID()); - - SendTraderItem( - buy_item->GetItem()->ID, - buy_item->IsStackable() ? outtbs->quantity : buy_item->GetCharges(), - t - ); - - if (RuleB(Bazaar, AuditTrail)) { - BazaarAuditTrail(Trader->GetName(), GetName(), buy_item->GetItem()->Name, outtbs->quantity, outtbs->price, 0); - } - - Trader->FindAndNukeTraderItem(sn, outtbs->quantity, this, 0); - - if (item_id > 0 && Trader->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - // Convert Serial Number back to ItemID for RoF+ - outtbs->item_id = item_id; - } - - Trader->QueuePacket(outapp.get()); } void Client::SendBazaarWelcome() @@ -1647,12 +1636,12 @@ static void UpdateTraderCustomerItemsAdded( database.CreateItem( i.item_id, i.item_charges, - i.aug_slot_1, - i.aug_slot_2, - i.aug_slot_3, - i.aug_slot_4, - i.aug_slot_5, - i.aug_slot_6 + i.augment_one, + i.augment_two, + i.augment_three, + i.augment_four, + i.augment_five, + i.augment_six ) ); if (!inst) { @@ -1661,15 +1650,14 @@ static void UpdateTraderCustomerItemsAdded( inst->SetCharges(i.item_charges); inst->SetPrice(i.item_cost); - inst->SetUniqueID(i.item_sn); - //FIXinst->SetMerchantSlot(i.item_sn); + inst->SetUniqueID(i.item_unique_id); if (inst->IsStackable()) { inst->SetMerchantCount(i.item_charges); } customer->SendItemPacket(EQ::invslot::slotCursor, inst.get(), ItemPacketMerchant); // MainCursor? LogTrading("Sending price update for [{}], Serial No. [{}] with [{}] charges", - item->Name, i.item_sn, i.item_charges); + item->Name, i.item_unique_id, i.item_charges); } } } @@ -1718,7 +1706,7 @@ static void UpdateTraderCustomerPriceChanged( // } //tdis->item_id = trader_items.at(i).item_sn; LogTrading("Telling customer to remove item [{}] with [{}] charges and S/N [{}]", - item_id, charges, trader_items.at(i).item_sn); + item_id, charges, trader_items.at(i).item_unique_id); customer->QueuePacket(outapp); } @@ -1735,12 +1723,12 @@ static void UpdateTraderCustomerPriceChanged( database.CreateItem( it->item_id, it->item_charges, - it->aug_slot_1, - it->aug_slot_2, - it->aug_slot_3, - it->aug_slot_4, - it->aug_slot_5, - it->aug_slot_6 + it->augment_one, + it->augment_two, + it->augment_three, + it->augment_four, + it->augment_five, + it->augment_six ) ); if (!inst) { @@ -1765,15 +1753,13 @@ static void UpdateTraderCustomerPriceChanged( continue; } - inst->SetUniqueID(trader_items.at(i).item_sn); - //inst->SetMerchantSlot(trader_items.at(i).item_sn); + inst->SetUniqueID(trader_items.at(i).item_unique_id); LogTrading("Sending price update for [{}], Serial No. [{}] with [{}] charges", - item->Name, trader_items.at(i).item_sn, trader_items.at(i).item_charges); + item->Name, trader_items.at(i).item_unique_id, trader_items.at(i).item_charges); customer->SendItemPacket(EQ::invslot::slotCursor, inst.get(), ItemPacketMerchant); // MainCursor?? } -// safe_delete(inst); } void Client::SendBuyerResults(BarterSearchRequest_Struct& bsr) @@ -2469,237 +2455,100 @@ void Client::SendTraderMode(BazaarTraderBarterActions status) safe_delete(outapp); } -void Client::TraderPriceUpdate(const EQApplicationPacket *app) +void Client::TraderUpdateItem(const EQApplicationPacket *app) { - // Handle price updates from the Trader and update a customer browsing our stuff if necessary - // This method also handles removing items from sale and adding them back up whilst still in - // Trader mode. - // - auto tpus = (TraderPriceUpdate_Struct *) app->pBuffer; + auto in = reinterpret_cast(app->pBuffer); + uint32 new_price = in->new_price; + auto inst = FindTraderItemByUniqueID(in->item_unique_id); + std::unique_ptr inst_copy(inst ? inst->Clone() : nullptr); - LogTrading( - "Received Price Update for [{}] Item Serial No. [{}] New Price [{}]", - GetName(), - tpus->serial_number, - tpus->NewPrice - ); - - // Pull the items this Trader currently has for sale from the trader table. - // - auto trader_items = TraderRepository::GetWhere(database, fmt::format("`char_id` = '{}'", CharacterID())); - uint32 item_limit = trader_items.size() >= GetInv().GetLookup()->InventoryTypeSize.Bazaar - ? GetInv().GetLookup()->InventoryTypeSize.Bazaar - : trader_items.size(); - - // The client only sends a single update with the Serial Number of the item whose price has been updated. - // We must update the price for all the Trader's items that are identical to that one item, i.e. - // if it is a stackable item like arrows, update the price for all stacks. If it is not stackable, then - // update the prices for all items that have the same number of charges. - // - uint32 id_of_item_to_update = 0; - int32 charges_on_item_to_update = 0; - uint32 old_price = 0; - - for (int i = 0; i < item_limit; i++) { - if ((trader_items.at(i).item_id > 0) && (trader_items.at(i).item_sn.compare(tpus->serial_number) == 0)) { - // We found the item that the Trader wants to change the price of (or add back up for sale). - // - id_of_item_to_update = trader_items.at(i).item_id; - charges_on_item_to_update = trader_items.at(i).item_charges; - old_price = trader_items.at(i).item_cost; - - LogTrading( - "ItemID is [{}] Charges is [{}]", - trader_items.at(i).item_id, - trader_items.at(i).item_charges - ); - break; - } - } - - if (id_of_item_to_update == 0) { - // If the item is not currently in the trader table for this Trader, then they must have removed it from sale while - // still in Trader mode. Check if the item is in their Trader Satchels, and if so, put it back up. - // Quick Sanity check. If the item is not currently up for sale, and the new price is zero, just ack the packet - // and do nothing. - if (tpus->NewPrice == 0) { - tpus->SubAction = BazaarPriceChange_RemoveItem; - QueuePacket(app); + if (new_price == 0) { + auto result = TraderRepository::DeleteWhere(database, fmt::format("`item_unique_id` = '{}'", in->item_unique_id)); + if (!result) { + LogError("Trader {} attempt to remove item_unique_id {} failed", CharacterID(), in->item_unique_id); return; } - LogTrading("Unable to find item to update price for. Rechecking trader satchels"); - - // Find what is in their Trader Satchels - auto newgis = GetTraderItems(); - uint32 id_of_item_to_add = 0; - int32 charges_on_item_to_add = 0; - - for (int i = 0; i < GetInv().GetLookup()->InventoryTypeSize.Bazaar; i++) { - if (newgis->items[i] > 0 && newgis->serial_number[i].compare(tpus->serial_number) == 0) { - id_of_item_to_add = newgis->items[i]; - charges_on_item_to_add = newgis->charges[i]; - - LogTrading( - "Found new Item to Add, ItemID is [{}] Charges is [{}]", - newgis->items[i], - newgis->charges[i] - ); - break; - } - } - - const EQ::ItemData *item = nullptr; - if (id_of_item_to_add) { - item = database.GetItem(id_of_item_to_add); - } - - if (!id_of_item_to_add || !item) { - tpus->SubAction = BazaarPriceChange_Fail; - QueuePacket(app); - TraderEndTrader(); - safe_delete(newgis); - - LogTrading("Item not found in Trader Satchels either"); - return; - } - - // It is a limitation of the client that if you have multiple of the same item, but with different charges, - // although you can set different prices for them before entering Trader mode. If you Remove them and then - // add them back whilst still in Trader mode, they all go up for the same price. We check for this situation - // and give the Trader a warning message. - // - if (!item->Stackable) { - bool same_item_with_differing_charges = false; - - for (int i = 0; i < GetInv().GetLookup()->InventoryTypeSize.Bazaar; i++) { - if ((newgis->items[i] == id_of_item_to_add) && (newgis->charges[i] != charges_on_item_to_add)) { - same_item_with_differing_charges = true; - break; - } - } - - if (same_item_with_differing_charges) { - Message( - Chat::Red, - "Warning: You have more than one %s with different charges. They have all been added for sale " - "at the same price.", - item->Name - ); - } - } - - // Now put all Items with a matching ItemID up for trade. - // - for (int i = 0; i < GetInv().GetLookup()->InventoryTypeSize.Bazaar; i++) { - if (newgis->items[i] == id_of_item_to_add) { - auto item_detail = FindTraderItemBySerialNumber(newgis->serial_number[i]); - - TraderRepository::Trader trader_item{}; - trader_item.id = 0; - trader_item.char_entity_id = GetID(); - trader_item.char_id = CharacterID(); - trader_item.char_zone_id = GetZoneID(); - trader_item.char_zone_instance_id = GetInstanceID(); - trader_item.item_charges = newgis->charges[i]; - trader_item.item_cost = tpus->NewPrice; - trader_item.item_id = newgis->items[i]; - trader_item.item_sn = newgis->serial_number[i]; - trader_item.listing_date = time(nullptr); - if (item_detail->IsAugmented()) { - auto augs = item_detail->GetAugmentIDs(); - trader_item.aug_slot_1 = augs.at(0); - trader_item.aug_slot_2 = augs.at(1); - trader_item.aug_slot_3 = augs.at(2); - trader_item.aug_slot_4 = augs.at(3); - trader_item.aug_slot_5 = augs.at(4); - trader_item.aug_slot_6 = augs.at(5); - } - trader_item.slot_id = i; - - TraderRepository::ReplaceOne(database, trader_item); - - trader_items.push_back(trader_item); - - LogTrading( - "Adding new item for [{}] ItemID [{}] SerialNumber [{}] Charges [{}] " - "Price: [{}] Slot [{}]", - GetName(), - newgis->items[i], - newgis->serial_number[i], - newgis->charges[i], - tpus->NewPrice, - i - ); - } - } - - // If we have a customer currently browsing, update them with the new items. - // - if (GetCustomerID()) { - UpdateTraderCustomerItemsAdded( - GetCustomerID(), - trader_items, - id_of_item_to_add, - GetInv().GetLookup()->InventoryTypeSize.Bazaar - ); - } - - safe_delete(newgis); - - // Acknowledge to the client. - tpus->SubAction = BazaarPriceChange_AddItem; + in->sub_action = BazaarPriceChange_RemoveItem; QueuePacket(app); + auto customer = entity_list.GetClientByID(GetCustomerID()); + if (customer && inst_copy) { + auto list = customer->GetTraderMerchantList(); + auto [slot_id, merchant_data] = customer->GetDataFromMerchantListByItemUniqueId(in->item_unique_id); + + auto client_packet = + new EQApplicationPacket(OP_ShopDelItem, static_cast(sizeof(Merchant_DelItem_Struct))); + auto client_data = reinterpret_cast(client_packet->pBuffer); + + client_data->npcid = GetID(); + client_data->playerid = customer->GetID(); + client_data->itemslot = slot_id; + + customer->QueuePacket(client_packet); + safe_delete(client_packet); + + list->erase(slot_id); + + customer->Message( + Chat::Red, + fmt::format( + "Trader {} removed item {} from the bazaar. Item no longer available.", + GetCleanName(), + inst_copy->GetItem()->Name + ).c_str() + ); + LogTrading("Trader removed item from trader list with item_unique_id {}", in->item_unique_id); + } + return; } - // This is a safeguard against a Trader increasing the price of an item while a customer is browsing and - // unwittingly buying it at a higher price than they were expecting to. - // - if ((old_price != 0) && (tpus->NewPrice > old_price) && GetCustomerID()) { - tpus->SubAction = BazaarPriceChange_Fail; - QueuePacket(app); - TraderEndTrader(); - Message( - Chat::Red, - "You must remove the item from sale before you can increase the price while a customer is browsing." - ); - Message(Chat::Red, "Click 'Begin Trader' to restart Trader mode with the increased price for this item."); - return; - } - - // Send Acknowledgement back to the client. - if (old_price == 0) { - tpus->SubAction = BazaarPriceChange_AddItem; - } - else if (tpus->NewPrice != 0) { - tpus->SubAction = BazaarPriceChange_UpdatePrice; - } - else { - tpus->SubAction = BazaarPriceChange_RemoveItem; + auto result = TraderRepository::UpdatePrice(database, in->item_unique_id, new_price); + if (!result && inst_copy) { + // Item does not exist so it must be re-created, + TraderRepository::Trader trader_item{}; + + trader_item.id = 0; + trader_item.char_entity_id = GetID(); + trader_item.character_id = CharacterID(); + trader_item.char_zone_id = GetZoneID(); + trader_item.char_zone_instance_id = GetInstanceID(); + trader_item.item_charges = inst_copy->GetCharges(); + trader_item.item_cost = new_price; + trader_item.item_id = inst_copy->GetID(); + trader_item.item_unique_id = in->item_unique_id; + trader_item.slot_id = 0; + trader_item.listing_date = time(nullptr); + if (inst_copy->IsAugmented()) { + auto augs = inst_copy->GetAugmentIDs(); + trader_item.augment_one = augs.at(0); + trader_item.augment_two = augs.at(1); + trader_item.augment_three = augs.at(2); + trader_item.augment_four = augs.at(3); + trader_item.augment_five = augs.at(4); + trader_item.augment_six = augs.at(5); + } + + TraderRepository::ReplaceOne(database, trader_item); } + in->sub_action = BazaarPriceChange_UpdatePrice; QueuePacket(app); - if (old_price == tpus->NewPrice) { - LogTrading("The new price is the same as the old one"); - return; - } - // Update the price for all items we have for sale that have this ItemID and number of charges, or remove - // them from the trader table if the new price is zero. - // - database.UpdateTraderItemPrice(CharacterID(), id_of_item_to_update, charges_on_item_to_update, tpus->NewPrice); + auto customer = entity_list.GetClientByID(GetCustomerID()); + if (customer && inst_copy) { + auto [slot_id, merchant_data] = customer->GetDataFromMerchantListByItemUniqueId(inst_copy->GetUniqueID()); + auto [merchant_quantity, item_unique_id] = merchant_data; - // If a customer is browsing our goods, send them the updated prices / remove the items from the Merchant window - if (GetCustomerID()) { - UpdateTraderCustomerPriceChanged( - GetCustomerID(), - trader_items, - id_of_item_to_update, - charges_on_item_to_update, - tpus->NewPrice, - item_limit + inst_copy->SetMerchantCount(merchant_quantity); + inst_copy->SetMerchantSlot(slot_id); + inst_copy->SetPrice(new_price); + customer->SendItemPacket(slot_id, inst_copy.get(), ItemPacketMerchant); + + customer->Message( + Chat::Red, + fmt::format("Trader {} updated the price of item {}", GetCleanName(), inst_copy->GetItem()->Name).c_str() ); } } @@ -2785,33 +2634,12 @@ void Client::SendBulkBazaarTraders() void Client::DoBazaarInspect(BazaarInspect_Struct &in) { - if (RuleB(Bazaar, UseAlternateBazaarSearch)) { - if (in.trader_id >= TraderRepository::TRADER_CONVERT_ID) { - auto sn = std::string(in.serial_number); - auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( - database, - in.trader_id - TraderRepository::TRADER_CONVERT_ID, - sn - ); - - if (!trader.trader_id) { - LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", - in.trader_id - TraderRepository::TRADER_CONVERT_ID, - in.serial_number - ); - return; - } - - in.trader_id = trader.trader_id; - } - } - auto items = TraderRepository::GetWhere( - database, fmt::format("`char_id` = '{}' AND `item_sn` = '{}'", in.trader_id, in.serial_number) + database, fmt::format("`item_unique_id` = '{}'", in.item_unique_id) ); if (items.empty()) { - LogInfo("Failed to find item with serial number [{}]", in.serial_number); + LogInfo("Failed to find item with serial number [{}]", in.item_unique_id); return; } @@ -2821,12 +2649,12 @@ void Client::DoBazaarInspect(BazaarInspect_Struct &in) database.CreateItem( item.item_id, item.item_charges, - item.aug_slot_1, - item.aug_slot_2, - item.aug_slot_3, - item.aug_slot_4, - item.aug_slot_5, - item.aug_slot_6 + item.augment_one, + item.augment_two, + item.augment_three, + item.augment_four, + item.augment_five, + item.augment_six ) ); @@ -2881,13 +2709,13 @@ std::string Client::DetermineMoneyString(uint64 cp) void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicationPacket *app) { auto in = (TraderBuy_Struct *) app->pBuffer; - auto sn = std::string(tbs->serial_number); + auto sn = std::string(tbs->item_unique_id); auto trader_item = TraderRepository::GetItemBySerialNumber(database, sn, tbs->trader_id); if (!trader_item.id || GetTraderTransactionDate() < trader_item.listing_date) { LogTrading("Attempt to purchase an item outside of the Bazaar trader_id [{}] item serial_number " "[{}] The Traders data was outdated.", tbs->trader_id, - tbs->serial_number + tbs->item_unique_id ); in->method = BazaarByParcel; in->sub_action = DataOutDated; @@ -2899,7 +2727,7 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati LogTrading("Attempt to purchase an item outside of the Bazaar trader_id [{}] item serial_number " "[{}] The item is already within an active transaction.", tbs->trader_id, - tbs->serial_number + tbs->item_unique_id ); in->method = BazaarByParcel; in->sub_action = DataOutDated; @@ -2913,19 +2741,19 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati database.CreateItem( trader_item.item_id, trader_item.item_charges, - trader_item.aug_slot_1, - trader_item.aug_slot_2, - trader_item.aug_slot_3, - trader_item.aug_slot_4, - trader_item.aug_slot_5, - trader_item.aug_slot_6 + trader_item.augment_one, + trader_item.augment_two, + trader_item.augment_three, + trader_item.augment_four, + trader_item.augment_five, + trader_item.augment_six ) ); if (!buy_item) { LogTrading("Unable to find item id [{}] item_sn [{}] on trader", trader_item.item_id, - trader_item.item_sn + trader_item.item_unique_id ); in->method = BazaarByParcel; in->sub_action = Failed; @@ -3087,13 +2915,6 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati if (trader_item.item_charges <= static_cast(tbs->quantity) || !buy_item->IsStackable()) { TraderRepository::DeleteOne(database, trader_item.id); - } else { - TraderRepository::UpdateQuantity( - database, - trader_item.char_id, - trader_item.item_sn, - trader_item.item_charges - tbs->quantity - ); } SendParcelDeliveryToWorld(ps); @@ -3878,3 +3699,43 @@ void Client::CancelTraderTradeWindow() auto end_session = new EQApplicationPacket(OP_ShopEnd); FastQueuePacket(&end_session); } + +void Client::AddDataToMerchantList(int16 slot_id, int32 quantity, const std::string &item_unique_id) +{ + auto list = GetTraderMerchantList(); + list->emplace(std::pair(slot_id, std::make_tuple(quantity, item_unique_id))); +} + +std::tuple Client::GetDataFromMerchantListByMerchantSlotId(int16 slot_id) +{ + auto list = GetTraderMerchantList(); + return list->contains(slot_id) ? list->at(slot_id) : std::make_tuple(INVALID_INDEX, "0000000000000000"); +} + +int16 Client::GetSlotFromMerchantListByItemUniqueId(const std::string &unique_id) +{ + auto list = GetTraderMerchantList(); + + for (auto [slot_id, merchant_data] : *list) { + auto [quantity, item_unique_id] = merchant_data; + if (item_unique_id == unique_id) { + return slot_id; + } + } + + return INVALID_INDEX; +} + +std::pair> Client::GetDataFromMerchantListByItemUniqueId(const std::string &unique_id) +{ + auto list = GetTraderMerchantList(); + + for (auto [slot_id, merchant_data] : *list) { + auto [quantity, item_unique_id] = merchant_data; + if (item_unique_id == unique_id) { + return { slot_id, merchant_data }; + } + } + + return std::make_pair(INVALID_INDEX, std::make_tuple(0, "0000000000000000")); +} diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 840979d8d..d17bfbfb7 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3791,8 +3791,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } + auto sn = std::string(in->trader_buy_struct.item_unique_id); auto outapp = std::make_unique(OP_Trader, static_cast(sizeof(TraderBuy_Struct))); - auto sn = std::string(in->trader_buy_struct.serial_number); auto data = (TraderBuy_Struct *) outapp->pBuffer; memcpy(data, &in->trader_buy_struct, sizeof(TraderBuy_Struct)); @@ -3826,7 +3826,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) RecordPlayerEventLogWithClient(trader_pc, PlayerEvent::TRADER_SELL, e); } - trader_pc->RemoveItemBySerialNumber(sn, in->trader_buy_struct.quantity); + trader_pc->RemoveItemByItemUniqueId(sn, in->trader_buy_struct.quantity); trader_pc->AddMoneyToPP(in->trader_buy_struct.price * in->trader_buy_struct.quantity, true); trader_pc->QueuePacket(outapp.get()); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 4df6e956d..20b62386c 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -343,12 +343,12 @@ std::unique_ptr ZoneDatabase::LoadSingleTraderItem(uint32 char database.CreateItem( item_id, charges, - results.at(0).aug_slot_1, - results.at(0).aug_slot_2, - results.at(0).aug_slot_3, - results.at(0).aug_slot_4, - results.at(0).aug_slot_5, - results.at(0).aug_slot_6 + results.at(0).augment_one, + results.at(0).augment_two, + results.at(0).augment_three, + results.at(0).augment_four, + results.at(0).augment_five, + results.at(0).augment_six ) ); if (!inst) {