summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/clif.c34
-rw-r--r--src/map/clif.h9
-rw-r--r--src/map/npc.c124
-rw-r--r--src/map/npc.h1
-rw-r--r--src/map/packets.h1
-rw-r--r--src/map/packets_struct.h20
6 files changed, 189 insertions, 0 deletions
diff --git a/src/map/clif.c b/src/map/clif.c
index 1d0ced6c0..2eb60e2e3 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -11578,6 +11578,10 @@ static void clif_parse_NpcBuySellSelected(int fd, struct map_session_data *sd)
/// 3 = "Out of the maximum capacity, you have too many items."
/// 9 = "Amounts are exceeded the possession of the item is not available for purchase."
/// 10 = "Props open-air store sales will be traded in RODEX"
+/// 11 = "The exchange failed."
+/// 12 = "The exchange was well done."
+/// 13 = "The item is already sold and out of stock."
+/// 14 = "There is not enough goods to exchange."
static void clif_npc_buy_result(struct map_session_data *sd, unsigned char result)
{
int fd;
@@ -22256,6 +22260,35 @@ static void clif_npc_barter_open(struct map_session_data *sd, struct npc_data *n
#endif
}
+static void clif_parse_NPCBarterPurchase(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
+static void clif_parse_NPCBarterPurchase(int fd, struct map_session_data *sd)
+{
+#if PACKETVER_ZERO_NUM >= 20181226
+ const struct PACKET_CZ_NPC_BARTER_PURCHASE *p = RP2PTR(fd);
+ int count = (p->packetLength - sizeof(struct PACKET_CZ_NPC_BARTER_PURCHASE)) / sizeof p->list[0];
+ struct barteritemlist item_list;
+
+ Assert_retv(count >= 0 && count <= sd->status.inventorySize);
+
+ VECTOR_INIT(item_list);
+ VECTOR_ENSURE(item_list, count, 1);
+
+ for (int i = 0; i < count; i++) {
+ struct barter_itemlist_entry entry = { 0 };
+ entry.addId = p->list[i].itemId;
+ entry.addAmount = p->list[i].amount;
+ entry.removeIndex = p->list[i].invIndex - 2;
+
+ VECTOR_PUSH(item_list, entry);
+ }
+
+ int response = npc->barter_buylist(sd, &item_list);
+ clif->npc_buy_result(sd, response);
+
+ VECTOR_CLEAR(item_list);
+#endif
+}
+
/*==========================================
* Main client packet processing function
*------------------------------------------*/
@@ -23444,4 +23477,5 @@ void clif_defaults(void)
clif->npc_barter_open = clif_npc_barter_open;
clif->pNPCBarterClosed = clif_parse_NPCBarterClosed;
+ clif->pNPCBarterPurchase = clif_parse_NPCBarterPurchase;
}
diff --git a/src/map/clif.h b/src/map/clif.h
index 44db8047f..3ed6e8ea1 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -665,6 +665,14 @@ struct stylist_data_entry {
};
VECTOR_DECL(struct stylist_data_entry) stylist_data[MAX_STYLIST_TYPE];
+struct barter_itemlist_entry {
+ int addId;
+ int addAmount;
+ int removeIndex;
+};
+
+VECTOR_STRUCT_DECL(barteritemlist, struct barter_itemlist_entry);
+
/**
* Stylist Shop Responds
**/
@@ -1589,6 +1597,7 @@ struct clif_interface {
void (*pReqRemainTime) (int fd, struct map_session_data *sd);
void (*npc_barter_open) (struct map_session_data *sd, struct npc_data *nd);
void (*pNPCBarterClosed) (int fd, struct map_session_data *sd);
+ void (*pNPCBarterPurchase) (int fd, struct map_session_data *sd);
};
#ifdef HERCULES_CORE
diff --git a/src/map/npc.c b/src/map/npc.c
index 4c4cd0bb5..7f003c321 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -2117,6 +2117,129 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item
return 0;
}
+/**
+ * Processes incoming npc barter purchase list
+ **/
+static int npc_barter_buylist(struct map_session_data *sd, struct barteritemlist *item_list)
+{
+ struct npc_data* nd;
+ struct npc_item_list *shop = NULL;
+ int w, new_;
+ unsigned short shop_size = 0;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ nd = npc->checknear(sd, map->id2bl(sd->npc_shopid));
+
+ if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_BARTER)
+ return 11;
+
+ shop = nd->u.scr.shop->item;
+ shop_size = nd->u.scr.shop->items;
+
+ w = 0;
+ new_ = 0;
+
+ int items[MAX_INVENTORY] = { 0 };
+
+ // process entries in buy list, one by one
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+
+ const int n = entry->removeIndex;
+ if (n < 0 || n >= sd->status.inventorySize)
+ return 11; // wrong inventory index
+
+ if (!itemdb->exists(entry->addId))
+ return 13; // item no longer in itemdb
+
+ const int removeId = sd->status.inventory[n].nameid;
+ int j = shop_size;
+ // find this entry in the shop's sell list
+ ARR_FIND(0, shop_size, j, (entry->addId == shop[j].nameid || entry->addId == itemdb_viewid(shop[j].nameid)) &&
+ (removeId == shop[j].value || removeId == itemdb_viewid(shop[j].value)));
+ if (j == shop_size)
+ return 13; // no such item in shop
+
+ const int removeAmount = shop[j].value2;
+
+ if (entry->addAmount > (int)shop[j].qty)
+ return 14; // not enough item amount in shop
+
+ if (removeAmount * entry->addAmount > sd->status.inventory[n].amount)
+ return 14; // not enough item amount in inventory
+
+ items[n] += removeAmount * entry->addAmount;
+
+ if (items[n] > sd->status.inventory[n].amount)
+ return 14; // not enough item amount in inventory
+
+ entry->addId = shop[j].nameid; //item_avail replacement
+
+ npc_market_qty[i] = j;
+
+ if (!itemdb->isstackable(entry->addId) && entry->addAmount > 1) {
+ //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, entry->addAmount, entry->addId);
+ entry->addAmount = 1;
+ }
+
+ switch (pc->checkadditem(sd, entry->addId, entry->addAmount)) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT: /* TODO find official response for this */
+ return 1;
+ }
+
+ w += itemdb_weight(entry->addId) * entry->addAmount;
+ w -= itemdb_weight(removeId) * removeAmount;
+ }
+
+ if (w + sd->weight > sd->max_weight)
+ return 2; // Too heavy
+
+ if (pc->inventoryblank(sd) < new_)
+ return 3; // Not enough space to store items
+
+ for (int i = 0; i < sd->status.inventorySize; ++i) {
+ const int removeAmountTotal = items[i];
+ if (removeAmountTotal == 0)
+ continue;
+ if (pc->delitem(sd, i, removeAmountTotal, 0, DELITEM_SOLD, LOG_TYPE_NPC) != 0) {
+ return 11; // unknown exploit
+ }
+ }
+
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+ const int shopIdx = npc_market_qty[i];
+
+ if (entry->addAmount > (int)shop[shopIdx].qty) /* wohoo someone tampered with the packet. */
+ return 14;
+
+ shop[shopIdx].qty -= entry->addAmount;
+
+ // TODO: save to sql
+
+ if (itemdb_type(entry->addId) == IT_PETEGG) {
+ pet->create_egg(sd, entry->addId);
+ } else {
+ struct item item_tmp;
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = entry->addId;
+ item_tmp.identify = 1;
+ pc->additem(sd, &item_tmp, entry->addAmount, LOG_TYPE_NPC);
+ }
+ }
+
+ return 12;
+}
+
/// npc_selllist for script-controlled shops
static int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd)
{
@@ -5308,6 +5431,7 @@ void npc_defaults(void)
npc->trader_pay = npc_trader_pay;
npc->trader_update = npc_trader_update;
npc->market_buylist = npc_market_buylist;
+ npc->barter_buylist = npc_barter_buylist;
npc->trader_open = npc_trader_open;
npc->market_fromsql = npc_market_fromsql;
npc->market_tosql = npc_market_tosql;
diff --git a/src/map/npc.h b/src/map/npc.h
index 2cd0c0e29..bd16938a3 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -310,6 +310,7 @@ struct npc_interface {
bool (*trader_pay) (struct npc_data *nd, struct map_session_data *sd, int price, int points);
void (*trader_update) (int master);
int (*market_buylist) (struct map_session_data *sd, struct itemlist *item_list);
+ int (*barter_buylist) (struct map_session_data *sd, struct barteritemlist *item_list);
bool (*trader_open) (struct map_session_data *sd, struct npc_data *nd);
void (*market_fromsql) (void);
void (*market_tosql) (struct npc_data *nd, unsigned short index);
diff --git a/src/map/packets.h b/src/map/packets.h
index aa9f041d3..44a49b387 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -1923,6 +1923,7 @@ packet(0x96e,clif->ackmergeitems);
#endif
#if PACKETVER_ZERO_NUM >= 20181226
+ packet(0x0b0f,clif->pNPCBarterPurchase);
packet(0x0b12,clif->pNPCBarterClosed);
#endif
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index 82ae82eaf..d20b20bee 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -3091,6 +3091,26 @@ struct PACKET_CZ_NPC_BARTER_CLOSE {
DEFINE_PACKET_HEADER(CZ_NPC_BARTER_CLOSE, 0x0b12);
#endif
+#if PACKETVER_ZERO_NUM >= 20181226
+struct PACKET_CZ_NPC_BARTER_PURCHASE_sub {
+#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114
+ uint32 itemId;
+#else
+ uint16 itemId;
+#endif
+ uint32 amount;
+ uint16 invIndex;
+ uint32 shopIndex;
+} __attribute__((packed));
+
+struct PACKET_CZ_NPC_BARTER_PURCHASE {
+ int16 packetType;
+ int16 packetLength;
+ struct PACKET_CZ_NPC_BARTER_PURCHASE_sub list[];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_NPC_BARTER_PURCHASE, 0x0b0f);
+#endif
+
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#pragma pack(pop)
#endif // not NetBSD < 6 / Solaris