Added support for unlimited supplies in market shops (#6571)
Fixes #6569 Thanks to @JohnnyPlayy, @Balferian and @Toshiro90 Co-authored-by: Aleos <aleos89@users.noreply.github.com>
This commit is contained in:
parent
3da8426fe7
commit
6ccf15330e
@ -291,7 +291,7 @@ these floating NPC objects are for. More on that below.
|
|||||||
-%TAB%pointshop%TAB%<NPC Name>%TAB%<sprite id>,<costvariable>{:<discount>},<itemid>:<price>{,<itemid>:<price>...}
|
-%TAB%pointshop%TAB%<NPC Name>%TAB%<sprite id>,<costvariable>{:<discount>},<itemid>:<price>{,<itemid>:<price>...}
|
||||||
<map name>,<x>,<y>,<facing>%TAB%pointshop%TAB%<NPC Name>%TAB%<sprite id>,<costvariable>{:<discount>},<itemid>:<price>{,<itemid>:<price>...}
|
<map name>,<x>,<y>,<facing>%TAB%pointshop%TAB%<NPC Name>%TAB%<sprite id>,<costvariable>{:<discount>},<itemid>:<price>{,<itemid>:<price>...}
|
||||||
|
|
||||||
<map name>,<x>,<y>,<facing>%TAB%marketshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>:<quantity>{,<itemid>:<price>:<quantity>...}
|
<map name>,<x>,<y>,<facing>%TAB%marketshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>:<stock>{,<itemid>:<price>:<stock>...}
|
||||||
|
|
||||||
Note: Additionally barter shops can be defined in npc/barters.yml
|
Note: Additionally barter shops can be defined in npc/barters.yml
|
||||||
|
|
||||||
@ -320,6 +320,9 @@ temporary character variables, permanent local account variables or permanent gl
|
|||||||
variables. These variables must be of integer type, not string. 'discount' flag is an
|
variables. These variables must be of integer type, not string. 'discount' flag is an
|
||||||
optional value which makes the price at that shop become affected by discount skill.
|
optional value which makes the price at that shop become affected by discount skill.
|
||||||
|
|
||||||
|
"marketshop" can have limited quantity of an item in stock.
|
||||||
|
Use -1 in the stock field to have unlimited stock in a marketshop.
|
||||||
|
|
||||||
** Define an warp/shop/cashshop/itemshop/pointshop/NPC duplicate.
|
** Define an warp/shop/cashshop/itemshop/pointshop/NPC duplicate.
|
||||||
|
|
||||||
warp/warp2: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<spanx>,<spany>
|
warp/warp2: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<spanx>,<spany>
|
||||||
@ -7254,6 +7257,7 @@ A full example of a dynamic shop can be found in doc/sample/npc_dynamic_shop.txt
|
|||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
*npcshopitem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};
|
*npcshopitem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};
|
||||||
|
*npcshopitem "<name>",<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,...}}};
|
||||||
|
|
||||||
This command lets you override the contents of an existing NPC shop or cashshop. The
|
This command lets you override the contents of an existing NPC shop or cashshop. The
|
||||||
current sell list will be wiped, and only the items specified with the price
|
current sell list will be wiped, and only the items specified with the price
|
||||||
@ -7263,12 +7267,12 @@ The function returns 1 if shop was updated successfully, or 0 if not found.
|
|||||||
|
|
||||||
NOTES:
|
NOTES:
|
||||||
- That you cannot use -1 to specify default selling price!
|
- That you cannot use -1 to specify default selling price!
|
||||||
- If attached shop type is market shop, need an extra param after price, it's <qty>
|
- If the attached shop type is a market shop, notice that there is an extra parameter after price, <stock>. Make sure to not add duplicate items! For unlimited stock use -1.
|
||||||
and make sure don't add duplication item!
|
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
*npcshopadditem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};
|
*npcshopadditem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};
|
||||||
|
*npcshopadditem "<name>",<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,...}}};
|
||||||
|
|
||||||
This command will add more items at the end of the selling list for the
|
This command will add more items at the end of the selling list for the
|
||||||
specified NPC shop or cashshop. If you specify an item already for sell, that item will
|
specified NPC shop or cashshop. If you specify an item already for sell, that item will
|
||||||
@ -7278,8 +7282,8 @@ The function returns 1 if shop was updated successfully, or 0 if not found.
|
|||||||
|
|
||||||
NOTES:
|
NOTES:
|
||||||
- That you cannot use -1 to specify default selling price!
|
- That you cannot use -1 to specify default selling price!
|
||||||
- If attached shop type is market shop, need an extra param after price, it's <qty>
|
- If attached shop type is market shop, need an extra param after price, it's <stock>
|
||||||
and make sure don't add duplication item!
|
and make sure don't add duplication item! For unlimited stock use -1.
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
@ -7317,9 +7321,9 @@ NOTES:
|
|||||||
|
|
||||||
*npcshopupdate "<name>",<item_id>,<price>{,<stock>}
|
*npcshopupdate "<name>",<item_id>,<price>{,<stock>}
|
||||||
|
|
||||||
Update an entry from shop. If price is 0 means don't change the price, maybe used for
|
Update an entry from a shop. If the price is 0 it won't be changed. May also be used for
|
||||||
marketshop to update the stock quantity. Except marketshop type, 'stock' value means
|
marketshop to update the stock quantity. For unlimited stock, use -1.
|
||||||
nothing.
|
For other shop types, the stock value has no effect.
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
@ -867,7 +867,7 @@ CREATE TABLE IF NOT EXISTS `market` (
|
|||||||
`name` varchar(50) NOT NULL DEFAULT '',
|
`name` varchar(50) NOT NULL DEFAULT '',
|
||||||
`nameid` int(10) UNSIGNED NOT NULL,
|
`nameid` int(10) UNSIGNED NOT NULL,
|
||||||
`price` INT(11) UNSIGNED NOT NULL,
|
`price` INT(11) UNSIGNED NOT NULL,
|
||||||
`amount` SMALLINT(5) UNSIGNED NOT NULL,
|
`amount` INT(11) NOT NULL,
|
||||||
`flag` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0',
|
`flag` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
PRIMARY KEY (`name`,`nameid`)
|
PRIMARY KEY (`name`,`nameid`)
|
||||||
) ENGINE = MyISAM;
|
) ENGINE = MyISAM;
|
||||||
|
3
sql-files/upgrades/upgrade_20220204.sql
Normal file
3
sql-files/upgrades/upgrade_20220204.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `market`
|
||||||
|
MODIFY `amount` INT(11) NOT NULL
|
||||||
|
;
|
@ -2283,6 +2283,11 @@ void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Out of stock
|
||||||
|
if( item->qty == 0 ){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
p->list[count].nameid = client_nameid( item->nameid );
|
p->list[count].nameid = client_nameid( item->nameid );
|
||||||
p->list[count].type = itemtype( item->nameid );
|
p->list[count].type = itemtype( item->nameid );
|
||||||
p->list[count].price = item->value;
|
p->list[count].price = item->value;
|
||||||
@ -2312,20 +2317,18 @@ void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) {
|
|||||||
|
|
||||||
/// Purchase item from Market shop.
|
/// Purchase item from Market shop.
|
||||||
/// 0x9d7 <packet len>.W <count>.B { <name id>.W <qty>.W <price>.L }* (ZC_NPC_MARKET_PURCHASE_RESULT)
|
/// 0x9d7 <packet len>.W <count>.B { <name id>.W <qty>.W <price>.L }* (ZC_NPC_MARKET_PURCHASE_RESULT)
|
||||||
void clif_npc_market_purchase_ack(struct map_session_data *sd, e_purchase_result res, uint8 n, struct s_npc_buy_list *list) {
|
void clif_npc_market_purchase_ack( struct map_session_data *sd, e_purchase_result res, std::vector<s_npc_buy_list>& list ){
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
nullpo_retv( sd );
|
nullpo_retv( sd );
|
||||||
nullpo_retv( list );
|
|
||||||
|
|
||||||
struct npc_data *nd = map_id2nd( sd->npc_shopid );
|
struct npc_data *nd = map_id2nd( sd->npc_shopid );
|
||||||
|
|
||||||
nullpo_retv( nd );
|
nullpo_retv( nd );
|
||||||
|
|
||||||
int fd = sd->fd;
|
struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *p = (struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *)packet_buffer;
|
||||||
|
|
||||||
WFIFOHEAD( fd, sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + n * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub ) );
|
|
||||||
struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *p = (struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *)WFIFOP( fd, 0 );
|
|
||||||
p->PacketType = HEADER_ZC_NPC_MARKET_PURCHASE_RESULT;
|
p->PacketType = HEADER_ZC_NPC_MARKET_PURCHASE_RESULT;
|
||||||
|
p->PacketLength = sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT );
|
||||||
|
|
||||||
#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814
|
#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814
|
||||||
p->result = ( res == e_purchase_result::PURCHASE_SUCCEED ? 0 : -1 );
|
p->result = ( res == e_purchase_result::PURCHASE_SUCCEED ? 0 : -1 );
|
||||||
@ -2333,10 +2336,8 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, e_purchase_result
|
|||||||
p->result = ( res == e_purchase_result::PURCHASE_SUCCEED ? 1 : 0 );
|
p->result = ( res == e_purchase_result::PURCHASE_SUCCEED ? 1 : 0 );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
if( p->result ){
|
if( p->result ){
|
||||||
for( int i = 0, j; i < n; i++ ){
|
for( int i = 0, j, count = 0; i < list.size(); i++ ){
|
||||||
ARR_FIND( 0, nd->u.shop.count, j, list[i].nameid == nd->u.shop.shop_item[j].nameid );
|
ARR_FIND( 0, nd->u.shop.count, j, list[i].nameid == nd->u.shop.shop_item[j].nameid );
|
||||||
|
|
||||||
// Not found
|
// Not found
|
||||||
@ -2347,12 +2348,12 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, e_purchase_result
|
|||||||
p->list[count].ITID = client_nameid( list[i].nameid );
|
p->list[count].ITID = client_nameid( list[i].nameid );
|
||||||
p->list[count].qty = list[i].qty;
|
p->list[count].qty = list[i].qty;
|
||||||
p->list[count].price = nd->u.shop.shop_item[j].value;
|
p->list[count].price = nd->u.shop.shop_item[j].value;
|
||||||
|
p->PacketLength += sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub );
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p->PacketLength = sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + count * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub );
|
clif_send( p, p->PacketLength, &sd->bl, SELF );
|
||||||
WFIFOSET( fd, p->PacketLength );
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2371,20 +2372,21 @@ void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) {
|
|||||||
|
|
||||||
int count = ( p->PacketLength - sizeof( struct packet_npc_market_purchase ) ) / sizeof( struct packet_npc_market_purchase_sub );
|
int count = ( p->PacketLength - sizeof( struct packet_npc_market_purchase ) ) / sizeof( struct packet_npc_market_purchase_sub );
|
||||||
|
|
||||||
struct s_npc_buy_list *list;
|
std::vector<s_npc_buy_list> items;
|
||||||
|
|
||||||
CREATE( list, struct s_npc_buy_list, count );
|
items.reserve( count );
|
||||||
|
|
||||||
// Sadly order is reverse
|
|
||||||
for( int i = 0; i < count; i++ ){
|
for( int i = 0; i < count; i++ ){
|
||||||
list[i].nameid = p->list[i].ITID;
|
s_npc_buy_list item = {};
|
||||||
list[i].qty = p->list[i].qty;
|
|
||||||
|
item.nameid = p->list[i].ITID;
|
||||||
|
item.qty = p->list[i].qty;
|
||||||
|
|
||||||
|
items.push_back( item );
|
||||||
}
|
}
|
||||||
|
|
||||||
e_purchase_result res = npc_buylist( sd, count, list );
|
e_purchase_result res = npc_buylist( sd, items );
|
||||||
clif_npc_market_purchase_ack( sd, res, count, list );
|
clif_npc_market_purchase_ack( sd, res, items );
|
||||||
|
|
||||||
aFree( list );
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12319,8 +12321,22 @@ void clif_parse_NpcBuyListSend( int fd, struct map_session_data* sd ){
|
|||||||
|
|
||||||
if( sd->state.trading || !sd->npc_shopid )
|
if( sd->state.trading || !sd->npc_shopid )
|
||||||
result = e_purchase_result::PURCHASE_FAIL_MONEY;
|
result = e_purchase_result::PURCHASE_FAIL_MONEY;
|
||||||
else
|
else{
|
||||||
result = npc_buylist( sd, n, (struct s_npc_buy_list*)p->items );
|
std::vector<s_npc_buy_list> items = {};
|
||||||
|
|
||||||
|
items.reserve( n );
|
||||||
|
|
||||||
|
for( uint16 i = 0; i < n; i++ ){
|
||||||
|
s_npc_buy_list item = {};
|
||||||
|
|
||||||
|
item.nameid = p->items[i].itemId;
|
||||||
|
item.qty = p->items[i].amount;
|
||||||
|
|
||||||
|
items.push_back( item );
|
||||||
|
}
|
||||||
|
|
||||||
|
result = npc_buylist( sd, items );
|
||||||
|
}
|
||||||
|
|
||||||
sd->npc_shopid = 0; //Clear shop data.
|
sd->npc_shopid = 0; //Clear shop data.
|
||||||
clif_npc_buy_result(sd, result);
|
clif_npc_buy_result(sd, result);
|
||||||
@ -17321,7 +17337,20 @@ void clif_parse_npccashshop_buy( int fd, struct map_session_data *sd ){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clif_cashshop_ack( sd, npc_cashshop_buylist( sd, p->kafraPoints, p->count, p->items ) );
|
std::vector<s_npc_buy_list> item_list = {};
|
||||||
|
|
||||||
|
item_list.reserve( p->count );
|
||||||
|
|
||||||
|
for( int i = 0; i < p->count; i++ ){
|
||||||
|
s_npc_buy_list item = {};
|
||||||
|
|
||||||
|
item.nameid = p->items[i].itemId;
|
||||||
|
item.qty = p->items[i].amount;
|
||||||
|
|
||||||
|
item_list.push_back( item );
|
||||||
|
}
|
||||||
|
|
||||||
|
clif_cashshop_ack( sd, npc_cashshop_buylist( sd, p->kafraPoints, item_list ) );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2411,8 +2411,7 @@ static enum e_CASHSHOP_ACK npc_cashshop_process_payment(struct npc_data *nd, int
|
|||||||
* @param item_list: List of items to purchase
|
* @param item_list: List of items to purchase
|
||||||
* @return clif_cashshop_ack value to display
|
* @return clif_cashshop_ack value to display
|
||||||
*/
|
*/
|
||||||
int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, struct PACKET_CZ_PC_BUY_CASH_POINT_ITEM_sub* item_list)
|
int npc_cashshop_buylist( struct map_session_data *sd, int points, std::vector<s_npc_buy_list>& item_list ){
|
||||||
{
|
|
||||||
int i, j, amount, new_, w, vt;
|
int i, j, amount, new_, w, vt;
|
||||||
t_itemid nameid;
|
t_itemid nameid;
|
||||||
struct npc_data *nd = (struct npc_data *)map_id2bl(sd->npc_shopid);
|
struct npc_data *nd = (struct npc_data *)map_id2bl(sd->npc_shopid);
|
||||||
@ -2429,10 +2428,10 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, str
|
|||||||
vt = 0; // Global Value
|
vt = 0; // Global Value
|
||||||
|
|
||||||
// Validating Process ----------------------------------------------------
|
// Validating Process ----------------------------------------------------
|
||||||
for( i = 0; i < count; i++ )
|
for( i = 0; i < item_list.size(); i++ )
|
||||||
{
|
{
|
||||||
nameid = item_list[i].itemId;
|
nameid = item_list[i].nameid;
|
||||||
amount = item_list[i].amount;
|
amount = item_list[i].qty;
|
||||||
id = itemdb_exists(nameid);
|
id = itemdb_exists(nameid);
|
||||||
|
|
||||||
if( !id || amount <= 0 )
|
if( !id || amount <= 0 )
|
||||||
@ -2442,12 +2441,12 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, str
|
|||||||
if( j == nd->u.shop.count || nd->u.shop.shop_item[j].value <= 0 )
|
if( j == nd->u.shop.count || nd->u.shop.shop_item[j].value <= 0 )
|
||||||
return ERROR_TYPE_ITEM_ID;
|
return ERROR_TYPE_ITEM_ID;
|
||||||
|
|
||||||
nameid = item_list[i].itemId = nd->u.shop.shop_item[j].nameid; //item_avail replacement
|
nameid = item_list[i].nameid = nd->u.shop.shop_item[j].nameid; //item_avail replacement
|
||||||
|
|
||||||
if( !itemdb_isstackable2(id) && amount > 1 )
|
if( !itemdb_isstackable2(id) && amount > 1 )
|
||||||
{
|
{
|
||||||
ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %u!\n", sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
|
ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %u!\n", sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
|
||||||
amount = item_list[i].amount = 1;
|
amount = item_list[i].qty = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( nd->master_nd ) { // Script-controlled shops decide by themselves, what can be bought and for what price.
|
if( nd->master_nd ) { // Script-controlled shops decide by themselves, what can be bought and for what price.
|
||||||
@ -2468,7 +2467,7 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, str
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nd->master_nd) //Script-based shops.
|
if (nd->master_nd) //Script-based shops.
|
||||||
return npc_buylist_sub(sd,count,(struct s_npc_buy_list*)item_list,nd->master_nd);
|
return npc_buylist_sub(sd,item_list,nd->master_nd);
|
||||||
|
|
||||||
if( w + sd->weight > sd->max_weight )
|
if( w + sd->weight > sd->max_weight )
|
||||||
return ERROR_TYPE_INVENTORY_WEIGHT;
|
return ERROR_TYPE_INVENTORY_WEIGHT;
|
||||||
@ -2480,9 +2479,9 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, str
|
|||||||
return res;
|
return res;
|
||||||
|
|
||||||
// Delivery Process ----------------------------------------------------
|
// Delivery Process ----------------------------------------------------
|
||||||
for( i = 0; i < count; i++ ) {
|
for( i = 0; i < item_list.size(); i++ ) {
|
||||||
nameid = item_list[i].itemId;
|
nameid = item_list[i].nameid;
|
||||||
amount = item_list[i].amount;
|
amount = item_list[i].qty;
|
||||||
|
|
||||||
if( !pet_create_egg(sd,nameid) ) {
|
if( !pet_create_egg(sd,nameid) ) {
|
||||||
struct item item_tmp;
|
struct item item_tmp;
|
||||||
@ -2663,16 +2662,16 @@ int npc_cashshop_buy(struct map_session_data *sd, t_itemid nameid, int amount, i
|
|||||||
* @param item_list: List of items
|
* @param item_list: List of items
|
||||||
* @param nd: Attached NPC
|
* @param nd: Attached NPC
|
||||||
*/
|
*/
|
||||||
static int npc_buylist_sub(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list, struct npc_data* nd) {
|
static int npc_buylist_sub(struct map_session_data* sd, std::vector<s_npc_buy_list>& item_list, struct npc_data* nd) {
|
||||||
char npc_ev[EVENT_NAME_LENGTH];
|
char npc_ev[EVENT_NAME_LENGTH];
|
||||||
int i, key_nameid = 0, key_amount = 0;
|
int key_nameid = 0, key_amount = 0;
|
||||||
|
|
||||||
// discard old contents
|
// discard old contents
|
||||||
script_cleararray_pc( sd, "@bought_nameid" );
|
script_cleararray_pc( sd, "@bought_nameid" );
|
||||||
script_cleararray_pc( sd, "@bought_quantity" );
|
script_cleararray_pc( sd, "@bought_quantity" );
|
||||||
|
|
||||||
// save list of bought items
|
// save list of bought items
|
||||||
for (i = 0; i < n; i++) {
|
for( int i = 0; i < item_list.size(); i++ ){
|
||||||
script_setarray_pc( sd, "@bought_nameid", i, item_list[i].nameid, &key_nameid );
|
script_setarray_pc( sd, "@bought_nameid", i, item_list[i].nameid, &key_nameid );
|
||||||
script_setarray_pc( sd, "@bought_quantity", i, item_list[i].qty, &key_amount );
|
script_setarray_pc( sd, "@bought_quantity", i, item_list[i].qty, &key_amount );
|
||||||
}
|
}
|
||||||
@ -2691,23 +2690,23 @@ static int npc_buylist_sub(struct map_session_data* sd, uint16 n, struct s_npc_b
|
|||||||
* @param item_list: List of items
|
* @param item_list: List of items
|
||||||
* @return result code for clif_parse_NpcBuyListSend/clif_npc_market_purchase_ack
|
* @return result code for clif_parse_NpcBuyListSend/clif_npc_market_purchase_ack
|
||||||
*/
|
*/
|
||||||
e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list) {
|
e_purchase_result npc_buylist( struct map_session_data* sd, std::vector<s_npc_buy_list>& item_list ){
|
||||||
struct npc_data* nd;
|
struct npc_data* nd;
|
||||||
struct npc_item_list *shop = NULL;
|
struct npc_item_list *shop = NULL;
|
||||||
double z;
|
double z;
|
||||||
int i,j,k,w,skill,new_;
|
int j,k,w,skill,new_;
|
||||||
uint8 market_index[MAX_INVENTORY];
|
uint8 market_index[MAX_INVENTORY];
|
||||||
|
|
||||||
nullpo_retr(e_purchase_result::PURCHASE_FAIL_COUNT, sd);
|
nullpo_retr(e_purchase_result::PURCHASE_FAIL_COUNT, sd);
|
||||||
nullpo_retr(e_purchase_result::PURCHASE_FAIL_COUNT, item_list);
|
|
||||||
|
|
||||||
nd = npc_checknear(sd,map_id2bl(sd->npc_shopid));
|
nd = npc_checknear(sd,map_id2bl(sd->npc_shopid));
|
||||||
if( nd == NULL )
|
if( nd == NULL )
|
||||||
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
||||||
if( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_MARKETSHOP )
|
if( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_MARKETSHOP )
|
||||||
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
||||||
if (!item_list || !n)
|
if( item_list.empty() ){
|
||||||
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
z = 0;
|
z = 0;
|
||||||
w = 0;
|
w = 0;
|
||||||
@ -2717,7 +2716,7 @@ e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_np
|
|||||||
|
|
||||||
memset(market_index, 0, sizeof(market_index));
|
memset(market_index, 0, sizeof(market_index));
|
||||||
// process entries in buy list, one by one
|
// process entries in buy list, one by one
|
||||||
for( i = 0; i < n; ++i ) {
|
for( int i = 0; i < item_list.size(); ++i ){
|
||||||
t_itemid nameid;
|
t_itemid nameid;
|
||||||
unsigned short amount;
|
unsigned short amount;
|
||||||
int value;
|
int value;
|
||||||
@ -2734,7 +2733,7 @@ e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_np
|
|||||||
|
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
if (item_list[i].qty > shop[j].qty)
|
if (shop[j].qty >= 0 && item_list[i].qty > shop[j].qty)
|
||||||
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
||||||
market_index[i] = j;
|
market_index[i] = j;
|
||||||
}
|
}
|
||||||
@ -2778,7 +2777,7 @@ e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_np
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nd->master_nd){ //Script-based shops.
|
if (nd->master_nd){ //Script-based shops.
|
||||||
npc_buylist_sub(sd,n,item_list,nd->master_nd);
|
npc_buylist_sub(sd,item_list,nd->master_nd);
|
||||||
return e_purchase_result::PURCHASE_SUCCEED;
|
return e_purchase_result::PURCHASE_SUCCEED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2792,17 +2791,20 @@ e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_np
|
|||||||
|
|
||||||
pc_payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
|
pc_payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
|
||||||
|
|
||||||
for( i = 0; i < n; ++i ) {
|
for( int i = 0; i < item_list.size(); ++i ) {
|
||||||
t_itemid nameid = item_list[i].nameid;
|
t_itemid nameid = item_list[i].nameid;
|
||||||
unsigned short amount = item_list[i].qty;
|
unsigned short amount = item_list[i].qty;
|
||||||
|
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
j = market_index[i];
|
j = market_index[i];
|
||||||
if (amount > shop[j].qty)
|
|
||||||
return e_purchase_result::PURCHASE_FAIL_MONEY;
|
if( shop[j].qty >= 0 ){
|
||||||
shop[j].qty -= amount;
|
if (amount > shop[j].qty)
|
||||||
npc_market_tosql(nd->exname, &shop[j]);
|
return e_purchase_result::PURCHASE_FAIL_COUNT;
|
||||||
|
shop[j].qty -= amount;
|
||||||
|
npc_market_tosql(nd->exname, &shop[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3979,7 +3981,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
nd->u.shop.count = 0;
|
nd->u.shop.count = 0;
|
||||||
while ( p ) {
|
while ( p ) {
|
||||||
t_itemid nameid2;
|
t_itemid nameid2;
|
||||||
unsigned short qty = 0;
|
int32 qty = -1;
|
||||||
int value;
|
int value;
|
||||||
struct item_data* id;
|
struct item_data* id;
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
@ -3989,7 +3991,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case NPCTYPE_MARKETSHOP:
|
case NPCTYPE_MARKETSHOP:
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
if (sscanf(p, ",%u:%11d:%6hu", &nameid2, &value, &qty) != 3) {
|
if (sscanf(p, ",%u:%11d:%11d", &nameid2, &value, &qty) != 3) {
|
||||||
ShowError("npc_parse_shop: (MARKETSHOP) Invalid item definition in file '%s', line '%d'. Ignoring the rest of the line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer, start - buffer), w1, w2, w3, w4);
|
ShowError("npc_parse_shop: (MARKETSHOP) Invalid item definition in file '%s', line '%d'. Ignoring the rest of the line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer, start - buffer), w1, w2, w3, w4);
|
||||||
skip = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
@ -4023,10 +4025,10 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
ShowWarning("npc_parse_shop: Item %s [%u] discounted buying price (%d->%d) is less than overcharged selling price (%d->%d) at file '%s', line '%d'.\n",
|
ShowWarning("npc_parse_shop: Item %s [%u] discounted buying price (%d->%d) is less than overcharged selling price (%d->%d) at file '%s', line '%d'.\n",
|
||||||
id->name.c_str(), nameid2, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer));
|
id->name.c_str(), nameid2, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer));
|
||||||
}
|
}
|
||||||
if (type == NPCTYPE_MARKETSHOP && (!qty || qty > UINT16_MAX)) {
|
if (type == NPCTYPE_MARKETSHOP && qty < -1) {
|
||||||
ShowWarning("npc_parse_shop: Item %s [%u] is stocked with invalid value %d, changed to 1. File '%s', line '%d'.\n",
|
ShowWarning("npc_parse_shop: Item %s [%u] is stocked with invalid value %hd, changed to unlimited (-1). File '%s', line '%d'.\n",
|
||||||
id->name.c_str(), nameid2, qty, filepath, strline(buffer,start-buffer));
|
id->name.c_str(), nameid2, qty, filepath, strline(buffer,start-buffer));
|
||||||
qty = 1;
|
qty = -1;
|
||||||
}
|
}
|
||||||
//for logs filters, atcommands and iteminfo script command
|
//for logs filters, atcommands and iteminfo script command
|
||||||
if( id->maxchance == 0 )
|
if( id->maxchance == 0 )
|
||||||
@ -4626,7 +4628,7 @@ int npc_instancedestroy(struct npc_data* nd)
|
|||||||
**/
|
**/
|
||||||
void npc_market_tosql(const char *exname, struct npc_item_list *list) {
|
void npc_market_tosql(const char *exname, struct npc_item_list *list) {
|
||||||
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
|
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
|
||||||
if (SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`name`,`nameid`,`price`,`amount`,`flag`) VALUES ('%s','%u','%d','%hu','%" PRIu8 "')",
|
if (SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`name`,`nameid`,`price`,`amount`,`flag`) VALUES ('%s','%u','%d','%d','%" PRIu8 "')",
|
||||||
market_table, exname, list->nameid, list->value, list->qty, list->flag) ||
|
market_table, exname, list->nameid, list->value, list->qty, list->flag) ||
|
||||||
SQL_ERROR == SqlStmt_Execute(stmt))
|
SQL_ERROR == SqlStmt_Execute(stmt))
|
||||||
SqlStmt_ShowDebug(stmt);
|
SqlStmt_ShowDebug(stmt);
|
||||||
@ -4709,7 +4711,7 @@ static int npc_market_checkall_sub(DBKey key, DBData *data, va_list ap) {
|
|||||||
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
||||||
}
|
}
|
||||||
else { // Removing "out-of-date" entry
|
else { // Removing "out-of-date" entry
|
||||||
ShowError("npc_market_checkall_sub: NPC '%s' does not sell item %u (qty %hu), deleting...\n", nd->exname, list->nameid, list->qty);
|
ShowError("npc_market_checkall_sub: NPC '%s' does not sell item %u (qty %d), deleting...\n", nd->exname, list->nameid, list->qty);
|
||||||
npc_market_delfromsql(nd->exname, list->nameid);
|
npc_market_delfromsql(nd->exname, list->nameid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,28 +33,16 @@ struct npc_item_list {
|
|||||||
t_itemid nameid;
|
t_itemid nameid;
|
||||||
unsigned int value;
|
unsigned int value;
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
unsigned short qty; ///< Stock counter (Market shop)
|
int32 qty; ///< Stock counter (Market shop)
|
||||||
uint8 flag; ///< 1: Item added by npcshopitem/npcshopadditem, force load! (Market shop)
|
uint8 flag; ///< 1: Item added by npcshopitem/npcshopadditem, force load! (Market shop)
|
||||||
#endif
|
#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(push, 1)
|
|
||||||
#endif // not NetBSD < 6 / Solaris
|
|
||||||
|
|
||||||
/// List of bought/sold item for NPC shops
|
/// List of bought/sold item for NPC shops
|
||||||
struct s_npc_buy_list {
|
struct s_npc_buy_list {
|
||||||
unsigned short qty; ///< Amount of item will be bought
|
int32 qty; ///< Amount of item will be bought
|
||||||
#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114
|
|
||||||
uint32 nameid; ///< ID of item will be bought
|
uint32 nameid; ///< ID of item will be bought
|
||||||
#else
|
};
|
||||||
uint16 nameid; ///< ID of item will be bought
|
|
||||||
#endif
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
struct s_stylist_costs{
|
struct s_stylist_costs{
|
||||||
uint32 price;
|
uint32 price;
|
||||||
@ -1491,8 +1479,8 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd);
|
|||||||
bool npc_scriptcont(struct map_session_data* sd, int id, bool closing);
|
bool npc_scriptcont(struct map_session_data* sd, int id, bool closing);
|
||||||
struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* bl);
|
struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* bl);
|
||||||
int npc_buysellsel(struct map_session_data* sd, int id, int type);
|
int npc_buysellsel(struct map_session_data* sd, int id, int type);
|
||||||
e_purchase_result npc_buylist(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list);
|
e_purchase_result npc_buylist(struct map_session_data* sd, std::vector<s_npc_buy_list>& item_list);
|
||||||
static int npc_buylist_sub(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list, struct npc_data* nd);
|
static int npc_buylist_sub(struct map_session_data* sd, std::vector<s_npc_buy_list>& item_list, struct npc_data* nd);
|
||||||
uint8 npc_selllist(struct map_session_data* sd, int n, unsigned short *item_list);
|
uint8 npc_selllist(struct map_session_data* sd, int n, unsigned short *item_list);
|
||||||
e_purchase_result npc_barter_purchase( struct map_session_data& sd, std::shared_ptr<s_npc_barter> barter, std::vector<s_barter_purchase>& purchases );
|
e_purchase_result npc_barter_purchase( struct map_session_data& sd, std::shared_ptr<s_npc_barter> barter, std::vector<s_barter_purchase>& purchases );
|
||||||
void npc_parse_mob2(struct spawn_data* mob);
|
void npc_parse_mob2(struct spawn_data* mob);
|
||||||
@ -1550,7 +1538,7 @@ void npc_shop_currency_type(struct map_session_data *sd, struct npc_data *nd, in
|
|||||||
|
|
||||||
extern struct npc_data* fake_nd;
|
extern struct npc_data* fake_nd;
|
||||||
|
|
||||||
int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, struct PACKET_CZ_PC_BUY_CASH_POINT_ITEM_sub* item_list);
|
int npc_cashshop_buylist( struct map_session_data *sd, int points, std::vector<s_npc_buy_list>& item_list );
|
||||||
bool npc_shop_discount(struct npc_data* nd);
|
bool npc_shop_discount(struct npc_data* nd);
|
||||||
|
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
|
@ -17653,9 +17653,17 @@ BUILDIN_FUNC(npcshopadditem)
|
|||||||
nd->u.shop.count++;
|
nd->u.shop.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 stock = script_getnum( st, i + 2 );
|
||||||
|
|
||||||
|
if( stock < -1 ){
|
||||||
|
ShowError( "builtin_npcshopadditem: Invalid stock amount in marketshop '%s'.\n", nd->exname );
|
||||||
|
script_pushint( st, 0 );
|
||||||
|
return SCRIPT_CMD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
nd->u.shop.shop_item[j].nameid = nameid;
|
nd->u.shop.shop_item[j].nameid = nameid;
|
||||||
nd->u.shop.shop_item[j].value = script_getnum(st,i+1);
|
nd->u.shop.shop_item[j].value = script_getnum(st,i+1);
|
||||||
nd->u.shop.shop_item[j].qty = script_getnum(st,i+2);
|
nd->u.shop.shop_item[j].qty = stock;
|
||||||
|
|
||||||
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
||||||
}
|
}
|
||||||
@ -23299,7 +23307,13 @@ BUILDIN_FUNC(npcshopupdate) {
|
|||||||
t_itemid nameid = script_getnum(st, 3);
|
t_itemid nameid = script_getnum(st, 3);
|
||||||
int price = script_getnum(st, 4);
|
int price = script_getnum(st, 4);
|
||||||
#if PACKETVER >= 20131223
|
#if PACKETVER >= 20131223
|
||||||
uint16 stock = script_hasdata(st,5) ? script_getnum(st,5) : 0;
|
int32 stock = script_hasdata(st,5) ? script_getnum(st,5) : -1;
|
||||||
|
|
||||||
|
if( stock < -1 ){
|
||||||
|
ShowError( "buildin_npcshopupdate: Invalid stock amount in marketshop '%s'.\n", nd->exname );
|
||||||
|
script_pushint( st, 0 );
|
||||||
|
return SCRIPT_CMD_FAILURE;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user