From fa1cb492536f2853c59a995dc54aa51ead420546 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Thu, 27 Dec 2018 21:42:14 +0300 Subject: Add barter type shop and add packet ZC_NPC_BARTER_OPEN --- db/constants.conf | 6 ------ doc/script_commands.txt | 5 +++++ src/map/clif.c | 38 ++++++++++++++++++++++++++++++++++ src/map/clif.h | 1 + src/map/npc.c | 15 +++++++++----- src/map/npc.h | 12 +++++++---- src/map/packets_struct.h | 28 +++++++++++++++++++++++++ src/map/script.c | 54 +++++++++++++++++++++++++++++++++++++++--------- 8 files changed, 134 insertions(+), 25 deletions(-) diff --git a/db/constants.conf b/db/constants.conf index 502091a0f..598bc89c4 100644 --- a/db/constants.conf +++ b/db/constants.conf @@ -3742,12 +3742,6 @@ constants_db: { IOT_PARTY: 2 IOT_GUILD: 3 - comment__: "tradertype" - NST_ZENY: 0 - NST_CASH: 1 - NST_MARKET: 2 - NST_CUSTOM: 3 - comment__: "strcharinfo" PC_NAME: 0 PC_PARTY: 1 diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 57d47b1b5..a545357aa 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -323,10 +323,12 @@ The types that a trader object can have are the following: - NST_MARKET (2) Normal NPC Market Shop (where items have limited availability and need to be refurbished) - NST_CUSTOM (3) Custom Shop (any currency, item/var/etca, check sample) +- NST_BARTER (4) Barter Shop (each item with own item currency) Unless otherwise specified via *tradertype an trader object will be defined as NST_ZENY. Note: NST_MARKET is only available with PACKETVER 20131223 or newer. +Note: NST_BARTER is only available with PACKETVER 20181226 zero or newer. See '12 - NPC Trader-Related Commands' and /doc/sample/npc_trader_sample.txt for more information regarding how to use this NPC type. @@ -9951,6 +9953,7 @@ when the optional NPC_Name param is used. --------------------------------------- *sellitem({, {, }}) +*sellitem(, , , ) adds (or modifies) data to the shop, when is not provided (or when it is -1) itemdb default is used. @@ -9959,6 +9962,8 @@ qty is only necessary for NST_MARKET trader types. when is already in the shop, the previous data (price/qty), is overwritten with the new. +currency_id and currency_amount can be used only with shop type NST_BARTER + --------------------------------------- *stopselling() diff --git a/src/map/clif.c b/src/map/clif.c index c25f5a9af..a0081ba6f 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -22215,6 +22215,41 @@ static bool clif_enchant_equipment(struct map_session_data *sd, enum equip_pos p #endif } +static void clif_npc_barter_open(struct map_session_data *sd, struct npc_data *nd) +{ +#if PACKETVER_ZERO_NUM >= 20181226 + nullpo_retv(sd); + nullpo_retv(nd); + struct npc_item_list *shop = nd->u.scr.shop->item; + const int shop_size = nd->u.scr.shop->items; + + int c = 0; + int maxCount = (sizeof(packet_buf) - sizeof(struct PACKET_ZC_NPC_BARTER_OPEN)) / sizeof(struct PACKET_ZC_NPC_BARTER_OPEN_sub); + struct PACKET_ZC_NPC_BARTER_OPEN *packet = (struct PACKET_ZC_NPC_BARTER_OPEN*)&packet_buf[0]; + packet->packetType = HEADER_ZC_NPC_BARTER_OPEN; + + for (int i = 0; i < shop_size && c < maxCount; i++) { + if (shop[i].nameid) { + struct item_data *id = itemdb->exists(shop[i].nameid); + if (id == NULL) + continue; + + packet->list[c].nameid = shop[i].nameid; + packet->list[c].type = itemtype(id->type); + packet->list[c].amount = shop[i].qty; + packet->list[c].currencyNameid = shop[i].value; + packet->list[c].currencyAmount = shop[i].value2; + packet->list[c].weight = id->weight * 10; + packet->list[c].index = i; + c++; + } + } + + packet->packetLength = sizeof(struct PACKET_ZC_NPC_BARTER_OPEN) + sizeof(struct PACKET_ZC_NPC_BARTER_OPEN_sub) * c; + clif->send(packet, packet->packetLength, &sd->bl, SELF); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -23400,4 +23435,7 @@ void clif_defaults(void) clif->pMemorialDungeonCommand = clif_parse_memorial_dungeon_command; clif->pReqRemainTime = clif_parse_reqRemainTime; + + clif->npc_barter_open = clif_npc_barter_open; + } diff --git a/src/map/clif.h b/src/map/clif.h index 91a91b5d3..f5c9f62e8 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1587,6 +1587,7 @@ struct clif_interface { void (*item_preview) (struct map_session_data *sd, int n); bool (*enchant_equipment) (struct map_session_data *sd, enum equip_pos pos, int cardSlot, int cardId); void (*pReqRemainTime) (int fd, struct map_session_data *sd); + void (*npc_barter_open) (struct map_session_data *sd, struct npc_data *nd); }; #ifdef HERCULES_CORE diff --git a/src/map/npc.c b/src/map/npc.c index fd11b6c5f..4c4cd0bb5 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1449,11 +1449,12 @@ static int npc_cashshop_buylist(struct map_session_data *sd, int points, struct return ERROR_TYPE_NPC; if( nd->subtype != CASHSHOP ) { - if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) { + if (nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET && nd->u.scr.shop->type != NST_BARTER) { shop = nd->u.scr.shop->item; shop_size = nd->u.scr.shop->items; - } else + } else { return ERROR_TYPE_NPC; + } } else { shop = nd->u.shop.shop_item; shop_size = nd->u.shop.count; @@ -1586,7 +1587,7 @@ static void npc_market_fromsql(void) ShowError("npc_market_fromsql: NPC '%s' not found! skipping...\n",name); npc->market_delfromsql_sub(name, USHRT_MAX); continue; - } else if ( nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET ) { + } else if (nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET) { ShowError("npc_market_fromsql: NPC '%s' is not proper for market, skipping...\n",name); npc->market_delfromsql_sub(name, USHRT_MAX); continue; @@ -1673,6 +1674,9 @@ static bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) clif->npc_market_open(sd,nd); } break; + case NST_BARTER: + clif->npc_barter_open(sd, nd); + break; default: clif->cashshop_show(sd,nd); break; @@ -1799,11 +1803,12 @@ static int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, return ERROR_TYPE_ITEM_ID; // Invalid Item if( nd->subtype != CASHSHOP ) { - if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) { + if (nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET && nd->u.scr.shop->type != NST_BARTER) { shop = nd->u.scr.shop->item; shop_size = nd->u.scr.shop->items; - } else + } else { return ERROR_TYPE_NPC; + } } else { shop = nd->u.shop.shop_item; shop_size = nd->u.shop.count; diff --git a/src/map/npc.h b/src/map/npc.h index ed5f4138d..2cd0c0e29 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -41,10 +41,11 @@ enum npc_parse_options { }; enum npc_shop_types { - NST_ZENY,/* default */ - NST_CASH,/* official npc cash shop */ - NST_MARKET,/* official npc market type */ + NST_ZENY, /* default */ + NST_CASH, /* official npc cash shop */ + NST_MARKET, /* official npc market type */ NST_CUSTOM, + NST_BARTER, /* official npc barter type */ /* */ NST_MAX, }; @@ -56,11 +57,14 @@ struct npc_label_list { char name[NAME_LENGTH]; int pos; }; + struct npc_item_list { int nameid; - unsigned int value; + unsigned int value; // price or barter currency item id + int value2; // barter currency item amount unsigned int qty; }; + struct npc_shop_data { unsigned char type;/* what am i */ struct npc_item_list *item;/* list */ diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 9fd30c0f6..1e11eadd7 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -3056,6 +3056,34 @@ struct PACKET_CZ_PARTY_CONFIG { } __attribute__((packed)); DEFINE_PACKET_HEADER(CZ_PARTY_CONFIG, 0x02c8); +#if PACKETVER_ZERO_NUM >= 20181226 +struct PACKET_ZC_NPC_BARTER_OPEN_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 nameid; +#else + uint16 nameid; +#endif + uint8 type; + uint32 amount; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 currencyNameid; +#else + uint16 currencyNameid; +#endif + uint32 currencyAmount; + uint32 weight; + uint32 index; +} __attribute__((packed)); + +struct PACKET_ZC_NPC_BARTER_OPEN { + int16 packetType; + int16 packetLength; + struct PACKET_ZC_NPC_BARTER_OPEN_sub list[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_NPC_BARTER_OPEN, 0x0b0e); +#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 diff --git a/src/map/script.c b/src/map/script.c index 25bf59839..d372d485e 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -23878,6 +23878,7 @@ static BUILDIN(sellitem) struct item_data *it; int i = 0, id = script_getnum(st,2); int value = 0; + int value2 = 0; int qty = 0; if( !(nd = map->id2nd(st->oid)) ) { @@ -23888,10 +23889,6 @@ static BUILDIN(sellitem) return false; } - value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; - if( value == -1 ) - value = it->value_buy; - if( !nd->u.scr.shop ) npc->trader_update(nd->src_id?nd->src_id:nd->bl.id); else {/* no need to run this if its empty */ @@ -23901,6 +23898,12 @@ static BUILDIN(sellitem) } } + if (nd->u.scr.shop->type != NST_BARTER) { + value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; + if( value == -1 ) + value = it->value_buy; + } + if( nd->u.scr.shop->type == NST_MARKET ) { if( !script_hasdata(st,4) || ( qty = script_getnum(st, 4) ) <= 0 ) { ShowError("buildin_sellitem: invalid 'qty' for market-type shop!\n"); @@ -23913,11 +23916,26 @@ static BUILDIN(sellitem) it->name, id, value, (int)(value*0.75), it->value_sell, (int)(it->value_sell*1.24), nd->exname, nd->path); } + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 5)) { + ShowError("buildin_sellitem: invalid number of parameters for barter-type shop!\n"); + return false; + } + qty = script_getnum(st, 3); + value = script_getnum(st, 4); + value2 = script_getnum(st, 5); + if (qty <= 0 || value <= 0 || value2 <= 0) { + ShowError("buildin_sellitem: invalid parameters for barter-type shop!\n"); + return false; + } + } + if( i != nd->u.scr.shop->items ) { nd->u.scr.shop->item[i].value = value; nd->u.scr.shop->item[i].qty = qty; - if( nd->u.scr.shop->type == NST_MARKET ) /* has been manually updated, make it reflect on sql */ - npc->market_tosql(nd,i); + if (nd->u.scr.shop->type == NST_MARKET) /* has been manually updated, make it reflect on sql */ + npc->market_tosql(nd, i); + // TODO: saving barter shop to sql } else { for( i = 0; i < nd->u.scr.shop->items; i++ ) { if( nd->u.scr.shop->item[i].nameid == 0 ) @@ -23935,6 +23953,7 @@ static BUILDIN(sellitem) nd->u.scr.shop->item[i].nameid = it->nameid; nd->u.scr.shop->item[i].value = value; + nd->u.scr.shop->item[i].value2 = value2; nd->u.scr.shop->item[i].qty = qty; } @@ -23966,11 +23985,13 @@ static BUILDIN(stopselling) if( i != nd->u.scr.shop->items ) { int cursor; - if( nd->u.scr.shop->type == NST_MARKET ) + if (nd->u.scr.shop->type == NST_MARKET) npc->market_delfromsql(nd,i); + // TODO: remove barter shop from sql nd->u.scr.shop->item[i].nameid = 0; nd->u.scr.shop->item[i].value = 0; + nd->u.scr.shop->item[i].value2 = 0; nd->u.scr.shop->item[i].qty = 0; for( i = 0, cursor = 0; i < nd->u.scr.shop->items; i++ ) { @@ -24054,6 +24075,12 @@ static BUILDIN(tradertype) script->reportsrc(st); } #endif +#if PACKETVER_ZERO_NUM < 20181226 + if (type == NST_BARTER) { + ShowWarning("buildin_tradertype: NST_BARTER is only available with PACKETVER_ZERO_NUM 20181226 or newer!\n"); + script->reportsrc(st); + } +#endif if( nd->u.scr.shop ) nd->u.scr.shop->type = type; @@ -24097,8 +24124,8 @@ static BUILDIN(shopcount) } else if ( !nd->u.scr.shop || !nd->u.scr.shop->items ) { ShowWarning("buildin_shopcount(%d): trying to use without any items!\n",id); return false; - } else if ( nd->u.scr.shop->type != NST_MARKET ) { - ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET shop!\n",id); + } else if (nd->u.scr.shop->type != NST_MARKET && nd->u.scr.shop->type != NST_BARTER) { + ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET and non-NST_BARTER shop!\n",id); return false; } @@ -25623,7 +25650,7 @@ static void script_parse_builtin(void) /* New Shop Support */ BUILDIN_DEF(openshop,"?"), - BUILDIN_DEF(sellitem,"i??"), + BUILDIN_DEF(sellitem,"i???"), BUILDIN_DEF(stopselling,"i"), BUILDIN_DEF(setcurrency,"i?"), BUILDIN_DEF(tradertype,"i"), @@ -26105,6 +26132,13 @@ static void script_hardcoded_constants(void) script->set_constant("EXPAND_INV_RESULT_MISSING_ITEM", EXPAND_INVENTORY_RESULT_MISSING_ITEM, false, false); script->set_constant("EXPAND_INV_RESULT_MAX_SIZE", EXPAND_INVENTORY_RESULT_MAX_SIZE, false, false); + script->constdb_comment("trader type"); + script->set_constant("NST_ZENY", NST_ZENY, false, false); + script->set_constant("NST_CASH", NST_CASH, false, false); + script->set_constant("NST_MARKET", NST_MARKET, false, false); + script->set_constant("NST_CUSTOM", NST_CUSTOM, false, false); + script->set_constant("NST_BARTER", NST_BARTER, false, false); + script->constdb_comment("Renewal"); #ifdef RENEWAL script->set_constant("RENEWAL", 1, false, false); -- cgit v1.2.3-70-g09d2