Merge pull request #364 from rathena/feature/market_shop
NPC Market Shop support * New shop script definition: `<map name>,<x>,<y>,<facing>%TAB%marketshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>:<quantity>{,<itemid>:<price>:<quantity>...}` * Added script command to update shop NPC: 'npcshopupdate "<name>",<itemid>,<price>{,<stock>}' * Added NPCMarketDB (DBMap) for market data persistance method. * Added `market_table` definition for market table in conf/inter_athena.conf. * Thank to @aleos89, @Lemongrass, @icxbb-xx, merged HerculesWS/Hercules@cf19b26. NOTES: * Minimum client needed 2013-12-23 (but this client has bugs here-and-there). * There's new table, see `upgrade_20150327_market.sql`. * Market shop doesn't support discount. * Added items by script `npcshopitem` or `npchopadditem` will be assumed as persistance items, will be loaded on next script reload or server start even market_shop NPC does't list them (unless NPC is not exists, entries will be removed).
This commit is contained in:
commit
40c63f403f
@ -138,6 +138,7 @@ mob_skill_db2_db: mob_skill_db2
|
|||||||
mapreg_db: mapreg
|
mapreg_db: mapreg
|
||||||
vending_db: vendings
|
vending_db: vendings
|
||||||
vending_items_db: vending_items
|
vending_items_db: vending_items
|
||||||
|
market_table: market
|
||||||
|
|
||||||
// Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
|
// Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
|
||||||
use_sql_db: no
|
use_sql_db: no
|
||||||
|
@ -537,7 +537,7 @@
|
|||||||
532: Shadow Right Accessory,
|
532: Shadow Right Accessory,
|
||||||
533: Shadow Left Accessory,
|
533: Shadow Left Accessory,
|
||||||
|
|
||||||
//534: // Free
|
534: Shop is out of stock! Please come back later.
|
||||||
|
|
||||||
// Bot detect messages (currently unused)
|
// Bot detect messages (currently unused)
|
||||||
535: Possible use of BOT (99%% of chance) or modified client by '%s' (account: %d, char_id: %d). This player ask your name when you are hidden.
|
535: Possible use of BOT (99%% of chance) or modified client by '%s' (account: %d, char_id: %d). This player ask your name when you are hidden.
|
||||||
|
@ -2310,10 +2310,12 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto]
|
|||||||
//0x097E,12 //ZC_UPDATE_RANKING_POINT
|
//0x097E,12 //ZC_UPDATE_RANKING_POINT
|
||||||
0x09B4,6,dull,0 //Cash Shop - Special Tab
|
0x09B4,6,dull,0 //Cash Shop - Special Tab
|
||||||
0x09CE,102,itemmonster,2
|
0x09CE,102,itemmonster,2
|
||||||
0x09D4,2,dull,0 //npcshopclosed
|
0x09D4,2,npcshopclosed,0
|
||||||
//NPC Market
|
//NPC Market
|
||||||
0x09D6,-1,dull,0 //npcmarketpurchase
|
0x09D5,-1
|
||||||
0x09D8,2,dull,0 //npcmarketclosed
|
0x09D6,-1,npcmarketpurchase,2:4:6
|
||||||
|
0x09D7,-1
|
||||||
|
0x09D8,2,npcmarketclosed,0
|
||||||
0x09DF,7
|
0x09DF,7
|
||||||
|
|
||||||
//Add new packets here
|
//Add new packets here
|
||||||
|
@ -282,6 +282,8 @@ 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>...}
|
||||||
|
|
||||||
This will define a shop NPC, which, when triggered (which can only be done by
|
This will define a shop NPC, which, when triggered (which can only be done by
|
||||||
clicking) will cause a shop window to come up. No code whatsoever runs in shop
|
clicking) will cause a shop window to come up. No code whatsoever runs in shop
|
||||||
NPCs and you can't change the prices otherwise than by editing the script
|
NPCs and you can't change the prices otherwise than by editing the script
|
||||||
@ -6239,7 +6241,10 @@ specified will be for sale.
|
|||||||
|
|
||||||
The function returns 1 if shop was updated successfully, or 0 if not found.
|
The function returns 1 if shop was updated successfully, or 0 if not found.
|
||||||
|
|
||||||
Note that you cannot use -1 to specify default selling price!
|
NOTES:
|
||||||
|
- 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>
|
||||||
|
and make sure don't add duplication item!
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
@ -6251,7 +6256,10 @@ appear twice on the sell list.
|
|||||||
|
|
||||||
The function returns 1 if shop was updated successfully, or 0 if not found.
|
The function returns 1 if shop was updated successfully, or 0 if not found.
|
||||||
|
|
||||||
Note that you cannot use -1 to specify default selling price!
|
NOTES:
|
||||||
|
- 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>
|
||||||
|
and make sure don't add duplication item!
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
@ -6282,6 +6290,17 @@ override any other script that may be already attached.
|
|||||||
|
|
||||||
The function returns 0 if the shop was not found, 1 otherwise.
|
The function returns 0 if the shop was not found, 1 otherwise.
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
- If attached shop type is market shop, will be default to call the 'buy' window.
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
*npcshopupdate "<name>",<item_id>,<price>{,<stock>}
|
||||||
|
|
||||||
|
Update an entry from shop. If price is 0 means don't change the price, maybe used for
|
||||||
|
marketshop to update the stock quantity. Except marketshop type, 'stock' value means
|
||||||
|
nothing.
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
*waitingroom "<chatroom name>",<limit>{,"<event label>"{,<trigger>{,<required zeny>{,<min lvl>{,<max lvl>}}}}};
|
*waitingroom "<chatroom name>",<limit>{,"<event label>"{,<trigger>{,<required zeny>{,<min lvl>{,<max lvl>}}}}};
|
||||||
|
@ -695,6 +695,10 @@ CREATE TABLE IF NOT EXISTS `storage` (
|
|||||||
KEY `account_id` (`account_id`)
|
KEY `account_id` (`account_id`)
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `interreg`
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `interreg` (
|
CREATE TABLE IF NOT EXISTS `interreg` (
|
||||||
`varname` varchar(11) NOT NULL,
|
`varname` varchar(11) NOT NULL,
|
||||||
`value` varchar(20) NOT NULL,
|
`value` varchar(20) NOT NULL,
|
||||||
@ -714,6 +718,10 @@ CREATE TABLE IF NOT EXISTS `bonus_script` (
|
|||||||
`icon` SMALLINT(3) NOT NULL DEFAULT '-1'
|
`icon` SMALLINT(3) NOT NULL DEFAULT '-1'
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `vending_items`
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `vending_items` (
|
CREATE TABLE IF NOT EXISTS `vending_items` (
|
||||||
`vending_id` int(10) unsigned NOT NULL,
|
`vending_id` int(10) unsigned NOT NULL,
|
||||||
`index` smallint(5) unsigned NOT NULL,
|
`index` smallint(5) unsigned NOT NULL,
|
||||||
@ -722,6 +730,10 @@ CREATE TABLE IF NOT EXISTS `vending_items` (
|
|||||||
`price` int(10) unsigned NOT NULL
|
`price` int(10) unsigned NOT NULL
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `vendings`
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `vendings` (
|
CREATE TABLE IF NOT EXISTS `vendings` (
|
||||||
`id` int(10) unsigned NOT NULL,
|
`id` int(10) unsigned NOT NULL,
|
||||||
`account_id` int(11) unsigned NOT NULL,
|
`account_id` int(11) unsigned NOT NULL,
|
||||||
@ -738,6 +750,10 @@ CREATE TABLE IF NOT EXISTS `vendings` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `buyingstore_items`
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `buyingstore_items` (
|
CREATE TABLE IF NOT EXISTS `buyingstore_items` (
|
||||||
`buyingstore_id` int(10) unsigned NOT NULL,
|
`buyingstore_id` int(10) unsigned NOT NULL,
|
||||||
`index` smallint(5) unsigned NOT NULL,
|
`index` smallint(5) unsigned NOT NULL,
|
||||||
@ -746,6 +762,10 @@ CREATE TABLE IF NOT EXISTS `buyingstore_items` (
|
|||||||
`price` int(10) unsigned NOT NULL
|
`price` int(10) unsigned NOT NULL
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `buyingstores`
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `buyingstores` (
|
CREATE TABLE IF NOT EXISTS `buyingstores` (
|
||||||
`id` int(10) unsigned NOT NULL,
|
`id` int(10) unsigned NOT NULL,
|
||||||
`account_id` int(11) unsigned NOT NULL,
|
`account_id` int(11) unsigned NOT NULL,
|
||||||
@ -762,3 +782,16 @@ CREATE TABLE IF NOT EXISTS `buyingstores` (
|
|||||||
`autotrade` tinyint(4) NOT NULL,
|
`autotrade` tinyint(4) NOT NULL,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table `market` for market shop persistency
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `market` (
|
||||||
|
`name` varchar(32) NOT NULL DEFAULT '',
|
||||||
|
`nameid` SMALLINT(5) UNSIGNED NOT NULL,
|
||||||
|
`price` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`amount` SMALLINT(5) UNSIGNED NOT NULL,
|
||||||
|
`flag` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
PRIMARY KEY (`name`,`nameid`)
|
||||||
|
) ENGINE = MyISAM;
|
||||||
|
12
sql-files/upgrades/upgrade_20150327_market.sql
Normal file
12
sql-files/upgrades/upgrade_20150327_market.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
--
|
||||||
|
-- Table `market` for market shop persistency
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `market` (
|
||||||
|
`name` varchar(32) NOT NULL DEFAULT '',
|
||||||
|
`nameid` SMALLINT(5) UNSIGNED NOT NULL,
|
||||||
|
`price` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`amount` SMALLINT(5) UNSIGNED NOT NULL,
|
||||||
|
`flag` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
PRIMARY KEY (`name`,`nameid`)
|
||||||
|
) ENGINE = MyISAM;
|
138
src/map/clif.c
138
src/map/clif.c
@ -1856,6 +1856,133 @@ void clif_selllist(struct map_session_data *sd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presents list of items, that can be sold to a Market shop.
|
||||||
|
* @author: Ind and Yommy
|
||||||
|
**/
|
||||||
|
void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) {
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
struct npc_item_list *shop = nd->u.shop.shop_item;
|
||||||
|
unsigned short shop_size = nd->u.shop.count, i, c, cmd = 0x9d5;
|
||||||
|
struct item_data *id = NULL;
|
||||||
|
struct s_packet_db *info;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if (sd->state.trading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
info = &packet_db[sd->packet_ver][cmd];
|
||||||
|
if (!info || info->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fd = sd->fd;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 4 + shop_size * 13);
|
||||||
|
WFIFOW(fd,0) = cmd;
|
||||||
|
|
||||||
|
for (i = 0, c = 0; i < shop_size; i++) {
|
||||||
|
if (shop[i].nameid && (id = itemdb_exists(shop[i].nameid))) {
|
||||||
|
WFIFOW(fd, 4+c*13) = shop[i].nameid;
|
||||||
|
WFIFOB(fd, 6+c*13) = itemtype(id->nameid);
|
||||||
|
WFIFOL(fd, 7+c*13) = shop[i].value;
|
||||||
|
WFIFOL(fd,11+c*13) = shop[i].qty;
|
||||||
|
WFIFOW(fd,15+c*13) = (id->view_id > 0) ? id->view_id : id->nameid;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WFIFOW(fd,2) = 4 + c*13;
|
||||||
|
WFIFOSET(fd,WFIFOW(fd,2));
|
||||||
|
sd->state.trading = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Closes the Market shop window.
|
||||||
|
void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) {
|
||||||
|
nullpo_retv(sd);
|
||||||
|
sd->npc_shopid = 0;
|
||||||
|
sd->state.trading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Purchase item from Market shop.
|
||||||
|
void clif_npc_market_purchase_ack(struct map_session_data *sd, uint8 res, uint8 n, struct s_npc_buy_list *list) {
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
unsigned short cmd = 0x9d7, len = 0;
|
||||||
|
struct npc_data* nd;
|
||||||
|
uint8 result = (res == 0 ? 1 : 0);
|
||||||
|
int fd = 0;
|
||||||
|
struct s_packet_db *info;
|
||||||
|
|
||||||
|
nullpo_retv(sd);
|
||||||
|
nullpo_retv((nd = map_id2nd(sd->npc_shopid)));
|
||||||
|
|
||||||
|
info = &packet_db[sd->packet_ver][cmd];
|
||||||
|
if (!info || info->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fd = sd->fd;
|
||||||
|
len = 5 + 8*n;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, len);
|
||||||
|
WFIFOW(fd, 0) = cmd;
|
||||||
|
WFIFOW(fd, 2) = len;
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
uint8 i, j;
|
||||||
|
struct npc_item_list *shop = nd->u.shop.shop_item;
|
||||||
|
unsigned short count = nd->u.shop.count;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
WFIFOW(fd, 5+i*8) = list[i].nameid;
|
||||||
|
WFIFOW(fd, 7+i*8) = list[i].qty;
|
||||||
|
|
||||||
|
ARR_FIND(0, count, j, list[i].nameid == shop[j].nameid);
|
||||||
|
WFIFOL(fd, 9+i*8) = (j != count) ? shop[j].value : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WFIFOB(fd, 4) = n;
|
||||||
|
WFIFOSET(fd, len);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Purchase item from Market shop.
|
||||||
|
void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) {
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
struct s_packet_db* info;
|
||||||
|
struct s_npc_buy_list *item_list;
|
||||||
|
uint16 cmd = RFIFOW(fd,0), len = 0, i = 0;
|
||||||
|
uint8 res = 0, n = 0;
|
||||||
|
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if (!sd->npc_shopid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||||
|
if (!info || info->len == 0)
|
||||||
|
return;
|
||||||
|
len = RFIFOW(fd,info->pos[0]);
|
||||||
|
n = (len-4) / 6;
|
||||||
|
|
||||||
|
CREATE(item_list, struct s_npc_buy_list, n);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
item_list[i].nameid = RFIFOW(fd,info->pos[1]+i*6);
|
||||||
|
item_list[i].qty = (uint16)min(RFIFOL(fd,info->pos[2]+i*6),USHRT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = npc_buylist(sd, n, item_list);
|
||||||
|
clif_npc_market_purchase_ack(sd, res, n, item_list);
|
||||||
|
aFree(item_list);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Displays an NPC dialog message (ZC_SAY_DIALOG).
|
/// Displays an NPC dialog message (ZC_SAY_DIALOG).
|
||||||
/// 00b4 <packet len>.W <npc id>.L <message>.?B
|
/// 00b4 <packet len>.W <npc id>.L <message>.?B
|
||||||
/// Client behavior (dialog window):
|
/// Client behavior (dialog window):
|
||||||
@ -10930,17 +11057,15 @@ void clif_npc_buy_result(struct map_session_data* sd, unsigned char result)
|
|||||||
void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd)
|
void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd)
|
||||||
{
|
{
|
||||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||||
int n = (RFIFOW(fd,info->pos[0])-4) /4;
|
uint16 n = (RFIFOW(fd,info->pos[0])-4) /4;
|
||||||
unsigned short* item_list = (unsigned short*)RFIFOP(fd,info->pos[1]);
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if( sd->state.trading || !sd->npc_shopid )
|
if( sd->state.trading || !sd->npc_shopid )
|
||||||
result = 1;
|
result = 1;
|
||||||
else
|
else
|
||||||
result = npc_buylist(sd,n,item_list);
|
result = npc_buylist(sd, n, (struct s_npc_buy_list*)RFIFOP(fd,info->pos[1]));
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17989,7 +18114,7 @@ void packetdb_readdb(bool reload)
|
|||||||
0, 0, 0, 0, 0, 0, 6, 4, 6, 4, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 6, 4, 6, 4, 0, 0, 0, 0, 0, 0,
|
||||||
//#0x09C0
|
//#0x09C0
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0,102, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0,102, 0,
|
||||||
0, 0, 0, 0, 2, 0, -1, 0, 2, 0, 0, 0, 0, 0, 0, 7,
|
0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, 0, 0, 0, 0, 7,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
@ -18214,6 +18339,9 @@ void packetdb_readdb(bool reload)
|
|||||||
{ clif_parse_client_version, "clientversion"},
|
{ clif_parse_client_version, "clientversion"},
|
||||||
{ clif_parse_blocking_playcancel, "booking_playcancel"},
|
{ clif_parse_blocking_playcancel, "booking_playcancel"},
|
||||||
{ clif_parse_ranklist, "ranklist"},
|
{ clif_parse_ranklist, "ranklist"},
|
||||||
|
/* Market NPC */
|
||||||
|
{ clif_parse_NPCMarketClosed, "npcmarketclosed" },
|
||||||
|
{ clif_parse_NPCMarketPurchase, "npcmarketpurchase" },
|
||||||
{NULL,NULL}
|
{NULL,NULL}
|
||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
|
@ -455,6 +455,9 @@ void clif_fixpos(struct block_list *bl); // area
|
|||||||
void clif_npcbuysell(struct map_session_data* sd, int id); //self
|
void clif_npcbuysell(struct map_session_data* sd, int id); //self
|
||||||
void clif_buylist(struct map_session_data *sd, struct npc_data *nd); //self
|
void clif_buylist(struct map_session_data *sd, struct npc_data *nd); //self
|
||||||
void clif_selllist(struct map_session_data *sd); //self
|
void clif_selllist(struct map_session_data *sd); //self
|
||||||
|
void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd);
|
||||||
|
void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd);
|
||||||
|
void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd);
|
||||||
void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes); //self
|
void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes); //self
|
||||||
void clif_scriptnext(struct map_session_data *sd,int npcid); //self
|
void clif_scriptnext(struct map_session_data *sd,int npcid); //self
|
||||||
void clif_scriptclose(struct map_session_data *sd, int npcid); //self
|
void clif_scriptclose(struct map_session_data *sd, int npcid); //self
|
||||||
|
@ -69,6 +69,7 @@ char mob_skill_db_re_db[32] = "mob_skill_db_re";
|
|||||||
char mob_skill_db2_db[32] = "mob_skill_db2";
|
char mob_skill_db2_db[32] = "mob_skill_db2";
|
||||||
char vendings_db[32] = "vendings";
|
char vendings_db[32] = "vendings";
|
||||||
char vending_items_db[32] = "vending_items";
|
char vending_items_db[32] = "vending_items";
|
||||||
|
char market_table[32] = "market";
|
||||||
|
|
||||||
// log database
|
// log database
|
||||||
char log_db_ip[32] = "127.0.0.1";
|
char log_db_ip[32] = "127.0.0.1";
|
||||||
@ -3759,6 +3760,8 @@ int inter_config_read(char *cfgName)
|
|||||||
strcpy( vendings_db, w2 );
|
strcpy( vendings_db, w2 );
|
||||||
else if( strcmpi( w1, "vending_items_db" ) == 0 )
|
else if( strcmpi( w1, "vending_items_db" ) == 0 )
|
||||||
strcpy( vending_items_db, w2 );
|
strcpy( vending_items_db, w2 );
|
||||||
|
else if (strcmpi(w1, "market_table") == 0)
|
||||||
|
strcpy(market_table, w2);
|
||||||
else
|
else
|
||||||
//Map Server SQL DB
|
//Map Server SQL DB
|
||||||
if(strcmpi(w1,"map_server_ip")==0)
|
if(strcmpi(w1,"map_server_ip")==0)
|
||||||
|
@ -312,7 +312,8 @@ enum npc_subtype {
|
|||||||
NPCTYPE_CASHSHOP, /// Cashshop
|
NPCTYPE_CASHSHOP, /// Cashshop
|
||||||
NPCTYPE_ITEMSHOP, /// Itemshop
|
NPCTYPE_ITEMSHOP, /// Itemshop
|
||||||
NPCTYPE_POINTSHOP, /// Pointshop
|
NPCTYPE_POINTSHOP, /// Pointshop
|
||||||
NPCTYPE_TOMB /// Monster tomb
|
NPCTYPE_TOMB, /// Monster tomb
|
||||||
|
NPCTYPE_MARKETSHOP, /// Marketshop
|
||||||
};
|
};
|
||||||
|
|
||||||
enum e_race {
|
enum e_race {
|
||||||
@ -993,6 +994,7 @@ extern char mob_skill_db_re_db[32];
|
|||||||
extern char mob_skill_db2_db[32];
|
extern char mob_skill_db2_db[32];
|
||||||
extern char vendings_db[32];
|
extern char vendings_db[32];
|
||||||
extern char vending_items_db[32];
|
extern char vending_items_db[32];
|
||||||
|
extern char market_table[32];
|
||||||
|
|
||||||
void do_shutdown(void);
|
void do_shutdown(void);
|
||||||
|
|
||||||
|
425
src/map/npc.c
425
src/map/npc.c
@ -40,6 +40,20 @@ static int npc_mob=0;
|
|||||||
static int npc_delay_mob=0;
|
static int npc_delay_mob=0;
|
||||||
static int npc_cache_mob=0;
|
static int npc_cache_mob=0;
|
||||||
|
|
||||||
|
// Market Shop
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
struct s_npc_market {
|
||||||
|
struct npc_item_list *list;
|
||||||
|
char exname[NAME_LENGTH+1];
|
||||||
|
uint16 count;
|
||||||
|
};
|
||||||
|
static DBMap *NPCMarketDB; /// Stock persistency! Temporary market stocks from `market` table. struct s_npc_market, key: NPC exname
|
||||||
|
static void npc_market_checkall(void);
|
||||||
|
static void npc_market_fromsql(void);
|
||||||
|
#define npc_market_delfromsql(exname,nameid) (npc_market_delfromsql_((exname), (nameid), false))
|
||||||
|
#define npc_market_clearfromsql(exname) (npc_market_delfromsql_((exname), 0, true))
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Returns a new npc id that isn't being used in id_db.
|
/// Returns a new npc id that isn't being used in id_db.
|
||||||
/// Fatal error if nothing is available.
|
/// Fatal error if nothing is available.
|
||||||
int npc_get_new_npc_id(void) {
|
int npc_get_new_npc_id(void) {
|
||||||
@ -1224,6 +1238,26 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd)
|
|||||||
case NPCTYPE_CASHSHOP:
|
case NPCTYPE_CASHSHOP:
|
||||||
clif_cashshop_show(sd,nd);
|
clif_cashshop_show(sd,nd);
|
||||||
break;
|
break;
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
{
|
||||||
|
unsigned short i;
|
||||||
|
|
||||||
|
for (i = 0; i < nd->u.shop.count; i++) {
|
||||||
|
if (nd->u.shop.shop_item[i].qty)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == nd->u.shop.count) {
|
||||||
|
clif_colormes(sd, color_table[COLOR_RED], msg_txt(sd, 534));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd->npc_shopid = nd->bl.id;
|
||||||
|
clif_npc_market_open(sd, nd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
case NPCTYPE_SCRIPT:
|
case NPCTYPE_SCRIPT:
|
||||||
run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
|
run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
|
||||||
break;
|
break;
|
||||||
@ -1318,7 +1352,7 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type)
|
|||||||
sd->state.callshop = 0;
|
sd->state.callshop = 0;
|
||||||
sd->npc_shopid = id;
|
sd->npc_shopid = id;
|
||||||
|
|
||||||
if (type==0) {
|
if (type == 0) {
|
||||||
clif_buylist(sd,nd);
|
clif_buylist(sd,nd);
|
||||||
} else {
|
} else {
|
||||||
clif_selllist(sd);
|
clif_selllist(sd);
|
||||||
@ -1410,9 +1444,14 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//npc_buylist for script-controlled shops.
|
/**
|
||||||
static int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd)
|
* npc_buylist for script-controlled shops.
|
||||||
{
|
* @param sd Player who bought
|
||||||
|
* @param n Number of item
|
||||||
|
* @param item_list List of item
|
||||||
|
* @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) {
|
||||||
char npc_ev[EVENT_NAME_LENGTH];
|
char npc_ev[EVENT_NAME_LENGTH];
|
||||||
int i;
|
int i;
|
||||||
int key_nameid = 0;
|
int key_nameid = 0;
|
||||||
@ -1423,10 +1462,9 @@ static int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* i
|
|||||||
script_cleararray_pc(sd, "@bought_quantity", (void*)0);
|
script_cleararray_pc(sd, "@bought_quantity", (void*)0);
|
||||||
|
|
||||||
// save list of bought items
|
// save list of bought items
|
||||||
for( i = 0; i < n; i++ )
|
for (i = 0; i < n; i++) {
|
||||||
{
|
script_setarray_pc(sd, "@bought_nameid", i, (void*)(intptr_t)item_list[i].nameid, &key_nameid);
|
||||||
script_setarray_pc(sd, "@bought_nameid", i, (void*)(intptr_t)item_list[i*2+1], &key_nameid);
|
script_setarray_pc(sd, "@bought_quantity", i, (void*)(intptr_t)item_list[i].qty, &key_amount);
|
||||||
script_setarray_pc(sd, "@bought_quantity", i, (void*)(intptr_t)item_list[i*2], &key_amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke event
|
// invoke event
|
||||||
@ -1519,16 +1557,20 @@ int npc_cashshop_buy(struct map_session_data *sd, unsigned short nameid, int amo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Player item purchase from npc shop.
|
/**
|
||||||
///
|
* Shop buylist
|
||||||
/// @param item_list 'n' pairs <amount,itemid>
|
* @param sd Player who attempt to buy
|
||||||
/// @return result code for clif_parse_NpcBuyListSend
|
* @param n Number of item will be bought
|
||||||
int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
* @param *item_list List of item will be bought
|
||||||
{
|
* @return result code for clif_parse_NpcBuyListSend/clif_npc_market_purchase_ack
|
||||||
|
**/
|
||||||
|
uint8 npc_buylist(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list) {
|
||||||
struct npc_data* nd;
|
struct npc_data* nd;
|
||||||
|
struct npc_item_list *shop = NULL;
|
||||||
double z;
|
double z;
|
||||||
int i,j,k,w,skill,new_,count = 0;
|
int i,j,k,w,skill,new_,count = 0;
|
||||||
char output[CHAT_SIZE_MAX];
|
char output[CHAT_SIZE_MAX];
|
||||||
|
uint8 market_index[MAX_INVENTORY];
|
||||||
|
|
||||||
nullpo_retr(3, sd);
|
nullpo_retr(3, sd);
|
||||||
nullpo_retr(3, item_list);
|
nullpo_retr(3, item_list);
|
||||||
@ -1536,48 +1578,58 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* 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 3;
|
return 3;
|
||||||
if( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP )
|
if( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP )
|
||||||
|
return 3;
|
||||||
|
if (!item_list || !n)
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
z = 0;
|
z = 0;
|
||||||
w = 0;
|
w = 0;
|
||||||
new_ = 0;
|
new_ = 0;
|
||||||
|
|
||||||
|
shop = nd->u.shop.shop_item;
|
||||||
|
|
||||||
|
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( i = 0; i < n; ++i ) {
|
||||||
{
|
unsigned short nameid, amount;
|
||||||
unsigned short nameid;
|
int value;
|
||||||
int amount, value;
|
|
||||||
|
|
||||||
// find this entry in the shop's sell list
|
// find this entry in the shop's sell list
|
||||||
ARR_FIND( 0, nd->u.shop.count, j,
|
ARR_FIND( 0, nd->u.shop.count, j,
|
||||||
item_list[i*2+1] == nd->u.shop.shop_item[j].nameid || //Normal items
|
item_list[i].nameid == shop[j].nameid || //Normal items
|
||||||
item_list[i*2+1] == itemdb_viewid(nd->u.shop.shop_item[j].nameid) //item_avail replacement
|
item_list[i].nameid == itemdb_viewid(shop[j].nameid) //item_avail replacement
|
||||||
);
|
);
|
||||||
|
|
||||||
if( j == nd->u.shop.count )
|
if( j == nd->u.shop.count )
|
||||||
return 3; // no such item in shop
|
return 3; // no such item in shop
|
||||||
|
|
||||||
amount = item_list[i*2+0];
|
#if PACKETVER >= 20131223
|
||||||
nameid = item_list[i*2+1] = nd->u.shop.shop_item[j].nameid; //item_avail replacement
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
value = nd->u.shop.shop_item[j].value;
|
if (item_list[i].qty > shop[j].qty)
|
||||||
|
return 3;
|
||||||
|
market_index[i] = j;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
amount = item_list[i].qty;
|
||||||
|
nameid = item_list[i].nameid = shop[j].nameid; //item_avail replacement
|
||||||
|
value = shop[j].value;
|
||||||
|
|
||||||
if( !itemdb_exists(nameid) )
|
if( !itemdb_exists(nameid) )
|
||||||
return 3; // item no longer in itemdb
|
return 3; // item no longer in itemdb
|
||||||
|
|
||||||
if( !itemdb_isstackable(nameid) && amount > 1 )
|
if( !itemdb_isstackable(nameid) && amount > 1 ) { //Exploit? You can't buy more than 1 of equipment types o.O
|
||||||
{ //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 nonstackable item %hu!\n",
|
ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %hu!\n",
|
||||||
sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
|
sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid);
|
||||||
amount = item_list[i*2+0] = 1;
|
amount = item_list[i].qty = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( nd->master_nd )
|
if( nd->master_nd ) { // Script-controlled shops decide by themselves, what can be bought and for what price.
|
||||||
{// Script-controlled shops decide by themselves, what can be bought and for what price.
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( pc_checkadditem(sd,nameid,amount) )
|
switch( pc_checkadditem(sd,nameid,amount) ) {
|
||||||
{
|
|
||||||
case CHKADDITEM_EXIST:
|
case CHKADDITEM_EXIST:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1596,11 +1648,12 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
|||||||
w += itemdb_weight(nameid) * amount;
|
w += itemdb_weight(nameid) * amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( nd->master_nd != NULL ) //Script-based shops.
|
if (nd->master_nd) //Script-based shops.
|
||||||
return npc_buylist_sub(sd,n,item_list,nd->master_nd);
|
return npc_buylist_sub(sd,n,item_list,nd->master_nd);
|
||||||
|
|
||||||
switch(nd->subtype) {
|
switch(nd->subtype) {
|
||||||
case NPCTYPE_SHOP:
|
case NPCTYPE_SHOP:
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
if (z > (double)sd->status.zeny)
|
if (z > (double)sd->status.zeny)
|
||||||
return 1; // Not enough Zeny
|
return 1; // Not enough Zeny
|
||||||
break;
|
break;
|
||||||
@ -1645,6 +1698,7 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
|||||||
|
|
||||||
switch(nd->subtype) {
|
switch(nd->subtype) {
|
||||||
case NPCTYPE_SHOP:
|
case NPCTYPE_SHOP:
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
pc_payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
|
pc_payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
|
||||||
break;
|
break;
|
||||||
case NPCTYPE_ITEMSHOP:
|
case NPCTYPE_ITEMSHOP:
|
||||||
@ -1666,16 +1720,24 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for( i = 0; i < n; ++i )
|
for( i = 0; i < n; ++i ) {
|
||||||
{
|
unsigned short nameid = item_list[i].nameid;
|
||||||
unsigned short nameid = item_list[i*2+1];
|
unsigned short amount = item_list[i].qty;
|
||||||
int amount = item_list[i*2+0];
|
|
||||||
struct item item_tmp;
|
struct item item_tmp;
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
j = market_index[i];
|
||||||
|
if (amount > shop[j].qty)
|
||||||
|
return 1;
|
||||||
|
shop[j].qty -= amount;
|
||||||
|
npc_market_tosql(nd->exname, &shop[j]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (itemdb_type(nameid) == IT_PETEGG)
|
if (itemdb_type(nameid) == IT_PETEGG)
|
||||||
pet_create_egg(sd, nameid);
|
pet_create_egg(sd, nameid);
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
memset(&item_tmp,0,sizeof(item_tmp));
|
memset(&item_tmp,0,sizeof(item_tmp));
|
||||||
item_tmp.nameid = nameid;
|
item_tmp.nameid = nameid;
|
||||||
item_tmp.identify = 1;
|
item_tmp.identify = 1;
|
||||||
@ -1685,14 +1747,12 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// custom merchant shop exp bonus
|
// custom merchant shop exp bonus
|
||||||
if( battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0 )
|
if( battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0 ) {
|
||||||
{
|
|
||||||
uint16 sk_idx = skill_get_index(MC_DISCOUNT);
|
uint16 sk_idx = skill_get_index(MC_DISCOUNT);
|
||||||
if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )
|
if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )
|
||||||
skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
||||||
|
|
||||||
if( skill > 0 )
|
if( skill > 0 ) {
|
||||||
{
|
|
||||||
z = z * (double)skill * (double)battle_config.shop_exp/10000.;
|
z = z * (double)skill * (double)battle_config.shop_exp/10000.;
|
||||||
if( z < 1 )
|
if( z < 1 )
|
||||||
z = 1;
|
z = 1;
|
||||||
@ -1708,7 +1768,6 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// npc_selllist for script-controlled shops
|
/// npc_selllist for script-controlled shops
|
||||||
static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd)
|
static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd)
|
||||||
{
|
{
|
||||||
@ -1771,7 +1830,7 @@ static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short*
|
|||||||
///
|
///
|
||||||
/// @param item_list 'n' pairs <index,amount>
|
/// @param item_list 'n' pairs <index,amount>
|
||||||
/// @return result code for clif_parse_NpcSellListSend
|
/// @return result code for clif_parse_NpcSellListSend
|
||||||
int 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)
|
||||||
{
|
{
|
||||||
double z;
|
double z;
|
||||||
int i,skill;
|
int i,skill;
|
||||||
@ -1957,7 +2016,7 @@ int npc_unload(struct npc_data* nd, bool single) {
|
|||||||
if( single && nd->bl.m != -1 )
|
if( single && nd->bl.m != -1 )
|
||||||
map_remove_questinfo(nd->bl.m, nd);
|
map_remove_questinfo(nd->bl.m, nd);
|
||||||
|
|
||||||
if( (nd->subtype == NPCTYPE_SHOP || nd->subtype == NPCTYPE_CASHSHOP || nd->subtype == NPCTYPE_ITEMSHOP || nd->subtype == NPCTYPE_POINTSHOP) && nd->src_id == 0) //src check for duplicate shops [Orcao]
|
if( (nd->subtype == NPCTYPE_SHOP || nd->subtype == NPCTYPE_CASHSHOP || nd->subtype == NPCTYPE_ITEMSHOP || nd->subtype == NPCTYPE_POINTSHOP || nd->subtype == NPCTYPE_MARKETSHOP) && nd->src_id == 0) //src check for duplicate shops [Orcao]
|
||||||
aFree(nd->u.shop.shop_item);
|
aFree(nd->u.shop.shop_item);
|
||||||
else if( nd->subtype == NPCTYPE_SCRIPT ) {
|
else if( nd->subtype == NPCTYPE_SCRIPT ) {
|
||||||
struct s_mapiterator* iter;
|
struct s_mapiterator* iter;
|
||||||
@ -2375,8 +2434,9 @@ static const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
* <map name>,<x>,<y>,<facing>%TAB%cashshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...}
|
* <map name>,<x>,<y>,<facing>%TAB%cashshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...}
|
||||||
* <map name>,<x>,<y>,<facing>%TAB%itemshop%TAB%<NPC Name>%TAB%<sprite id>,<costitemid>{:<discount>},<itemid>:<price>{,<itemid>:<price>...}
|
* <map name>,<x>,<y>,<facing>%TAB%itemshop%TAB%<NPC Name>%TAB%<sprite id>,<costitemid>{:<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>...}
|
||||||
* @param w1 : word 1 before tab (<from map name>,<x>,<y>,<facing>)
|
* @param w1 : word 1 before tab (<from map name>,<x>,<y>,<facing>)
|
||||||
* @param w2 : word 2 before tab (shop|cashshop|itemshop|pointshop), keyword that sent us in this parsing
|
* @param w2 : word 2 before tab (shop|cashshop|itemshop|pointshop|marketshop), keyword that sent us in this parsing
|
||||||
* @param w3 : word 3 before tab (<NPC Name>)
|
* @param w3 : word 3 before tab (<NPC Name>)
|
||||||
* @param w4 : word 4 before tab (<sprited id>,<shop definition...>)
|
* @param w4 : word 4 before tab (<sprited id>,<shop definition...>)
|
||||||
* @param start : index to start parsing
|
* @param start : index to start parsing
|
||||||
@ -2420,6 +2480,8 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
type = NPCTYPE_ITEMSHOP;
|
type = NPCTYPE_ITEMSHOP;
|
||||||
else if( !strcasecmp(w2,"pointshop") )
|
else if( !strcasecmp(w2,"pointshop") )
|
||||||
type = NPCTYPE_POINTSHOP;
|
type = NPCTYPE_POINTSHOP;
|
||||||
|
else if( !strcasecmp(w2, "marketshop") )
|
||||||
|
type = NPCTYPE_MARKETSHOP;
|
||||||
else
|
else
|
||||||
type = NPCTYPE_SHOP;
|
type = NPCTYPE_SHOP;
|
||||||
|
|
||||||
@ -2459,6 +2521,14 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
p = strchr(p+1,',');
|
p = strchr(p+1,',');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
|
#if PACKETVER < 20131223
|
||||||
|
ShowError("npc_parse_shop: (MARKETSHOP) Feature is disabled, need client 20131223 or newer. Ignoring file '%s', line '%d\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer, start - buffer), w1, w2, w3, w4);
|
||||||
|
return strchr(start, '\n'); // skip and continue
|
||||||
|
#else
|
||||||
|
is_discount = 0;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
is_discount = 1;
|
is_discount = 1;
|
||||||
break;
|
break;
|
||||||
@ -2468,25 +2538,43 @@ 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 ) {
|
||||||
unsigned short nameid2;
|
unsigned short nameid2, qty = 0;
|
||||||
int value;
|
int value, i = 0;
|
||||||
struct item_data* id;
|
struct item_data* id;
|
||||||
|
bool skip = false;
|
||||||
|
|
||||||
if( p == NULL )
|
if( p == NULL )
|
||||||
break;
|
break;
|
||||||
if( sscanf(p, ",%hu:%d", &nameid2, &value) != 2 ) {
|
switch(type) {
|
||||||
ShowError("npc_parse_shop: 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);
|
case NPCTYPE_SHOP:
|
||||||
break;
|
if (sscanf(p, ",%hu:%d", &nameid2, &value) != 2) {
|
||||||
|
ShowError("npc_parse_shop: (SHOP) 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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (sscanf(p, ",%hu:%d:%hu", &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);
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skip)
|
||||||
|
break;
|
||||||
|
|
||||||
if( (id = itemdb_exists(nameid2)) == NULL ) {
|
if( (id = itemdb_exists(nameid2)) == NULL ) {
|
||||||
ShowWarning("npc_parse_shop: Invalid sell item in file '%s', line '%d' (id '%hu').\n", filepath, strline(buffer,start-buffer), nameid2);
|
ShowWarning("npc_parse_shop: Invalid sell item in file '%s', line '%d' (id '%hu').\n", filepath, strline(buffer,start-buffer), nameid2);
|
||||||
p = strchr(p+1,',');
|
p = strchr(p+1,',');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if( value < 0 ) {
|
if( value < 0 ) {
|
||||||
if( type == NPCTYPE_SHOP ) value = id->value_buy;
|
if (type == NPCTYPE_SHOP || type == NPCTYPE_MARKETSHOP) value = id->value_buy;
|
||||||
else value = 0; // Cashshop doesn't have a "buy price" in the item_db
|
else value = 0; // Cashshop doesn't have a "buy price" in the item_db
|
||||||
}
|
}
|
||||||
if( (type == NPCTYPE_SHOP || type == NPCTYPE_ITEMSHOP || type == NPCTYPE_POINTSHOP) && value == 0 ) { // NPC selling items for free!
|
if (value == 0 && (type == NPCTYPE_SHOP || type == NPCTYPE_ITEMSHOP || type == NPCTYPE_POINTSHOP || type == NPCTYPE_MARKETSHOP)) { // NPC selling items for free!
|
||||||
ShowWarning("npc_parse_shop: Item %s [%hu] is being sold for FREE in file '%s', line '%d'.\n",
|
ShowWarning("npc_parse_shop: Item %s [%hu] is being sold for FREE in file '%s', line '%d'.\n",
|
||||||
id->name, nameid2, filepath, strline(buffer,start-buffer));
|
id->name, nameid2, filepath, strline(buffer,start-buffer));
|
||||||
}
|
}
|
||||||
@ -2494,17 +2582,37 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
ShowWarning("npc_parse_shop: Item %s [%hu] 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 [%hu] discounted buying price (%d->%d) is less than overcharged selling price (%d->%d) at file '%s', line '%d'.\n",
|
||||||
id->name, nameid2, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer));
|
id->name, 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)) {
|
||||||
|
ShowWarning("npc_parse_shop: Item %s [%hu] is stocked with invalid value %d, changed to 1. File '%s', line '%d'.\n",
|
||||||
|
id->name, nameid2, filepath, strline(buffer,start-buffer));
|
||||||
|
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 )
|
||||||
id->maxchance = -1; // -1 would show that the item's sold in NPC Shop
|
id->maxchance = -1; // -1 would show that the item's sold in NPC Shop
|
||||||
|
|
||||||
if (nd->u.shop.count > 0)
|
#if PACKETVER >= 20131223
|
||||||
RECREATE(nd->u.shop.shop_item, struct npc_item_list,nd->u.shop.count+1);
|
if (nd->u.shop.count && type == NPCTYPE_MARKETSHOP) {
|
||||||
else
|
// Duplicate entry? Replace the value
|
||||||
CREATE(nd->u.shop.shop_item, struct npc_item_list,1);
|
ARR_FIND(0, nd->u.shop.count, i, nd->u.shop.shop_item[i].nameid == nameid);
|
||||||
|
if (i != nd->u.shop.count) {
|
||||||
|
nd->u.shop.shop_item[i].qty = qty;
|
||||||
|
nd->u.shop.shop_item[i].value = value;
|
||||||
|
p = strchr(p+1,',');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RECREATE(nd->u.shop.shop_item, struct npc_item_list,nd->u.shop.count+1);
|
||||||
|
|
||||||
nd->u.shop.shop_item[nd->u.shop.count].nameid = nameid2;
|
nd->u.shop.shop_item[nd->u.shop.count].nameid = nameid2;
|
||||||
nd->u.shop.shop_item[nd->u.shop.count].value = value;
|
nd->u.shop.shop_item[nd->u.shop.count].value = value;
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
nd->u.shop.shop_item[nd->u.shop.count].flag = 0;
|
||||||
|
if (type == NPCTYPE_MARKETSHOP)
|
||||||
|
nd->u.shop.shop_item[nd->u.shop.count].qty = qty;
|
||||||
|
#endif
|
||||||
nd->u.shop.count++;
|
nd->u.shop.count++;
|
||||||
p = strchr(p+1,',');
|
p = strchr(p+1,',');
|
||||||
}
|
}
|
||||||
@ -2519,6 +2627,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
else if (type == NPCTYPE_POINTSHOP) safestrncpy(nd->u.shop.pointshop_str,point_str,strlen(point_str)+1); // Point shop currency
|
else if (type == NPCTYPE_POINTSHOP) safestrncpy(nd->u.shop.pointshop_str,point_str,strlen(point_str)+1); // Point shop currency
|
||||||
nd->u.shop.discount = is_discount;
|
nd->u.shop.discount = is_discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
nd->bl.prev = nd->bl.next = NULL;
|
nd->bl.prev = nd->bl.next = NULL;
|
||||||
nd->bl.m = m;
|
nd->bl.m = m;
|
||||||
nd->bl.x = x;
|
nd->bl.x = x;
|
||||||
@ -2531,6 +2640,14 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
|
|||||||
++npc_shop;
|
++npc_shop;
|
||||||
nd->bl.type = BL_NPC;
|
nd->bl.type = BL_NPC;
|
||||||
nd->subtype = type;
|
nd->subtype = type;
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
// Insert market data to table
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
uint16 i;
|
||||||
|
for (i = 0; i < nd->u.shop.count; i++)
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if( m >= 0 )
|
if( m >= 0 )
|
||||||
{// normal shop npc
|
{// normal shop npc
|
||||||
map_addnpc(m,nd);
|
map_addnpc(m,nd);
|
||||||
@ -2857,7 +2974,7 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch
|
|||||||
type = dnd->subtype;
|
type = dnd->subtype;
|
||||||
|
|
||||||
// get placement
|
// get placement
|
||||||
if( (type == NPCTYPE_SHOP || type == NPCTYPE_CASHSHOP || type == NPCTYPE_ITEMSHOP || type == NPCTYPE_POINTSHOP || type == NPCTYPE_SCRIPT) && strcmp(w1, "-") == 0 ) {// floating shop/chashshop/itemshop/pointshop/script
|
if ((type == NPCTYPE_SHOP || type == NPCTYPE_CASHSHOP || type == NPCTYPE_ITEMSHOP || type == NPCTYPE_POINTSHOP || type == NPCTYPE_SCRIPT || type == NPCTYPE_MARKETSHOP) && strcmp(w1, "-") == 0) {// floating shop/chashshop/itemshop/pointshop/script
|
||||||
x = y = dir = 0;
|
x = y = dir = 0;
|
||||||
m = -1;
|
m = -1;
|
||||||
} else {
|
} else {
|
||||||
@ -2908,6 +3025,7 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch
|
|||||||
case NPCTYPE_CASHSHOP:
|
case NPCTYPE_CASHSHOP:
|
||||||
case NPCTYPE_ITEMSHOP:
|
case NPCTYPE_ITEMSHOP:
|
||||||
case NPCTYPE_POINTSHOP:
|
case NPCTYPE_POINTSHOP:
|
||||||
|
case NPCTYPE_MARKETSHOP:
|
||||||
++npc_shop;
|
++npc_shop;
|
||||||
nd->u.shop.shop_item = dnd->u.shop.shop_item;
|
nd->u.shop.shop_item = dnd->u.shop.shop_item;
|
||||||
nd->u.shop.count = dnd->u.shop.count;
|
nd->u.shop.count = dnd->u.shop.count;
|
||||||
@ -3059,6 +3177,176 @@ int npc_instanceinit(struct npc_data* nd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
/**
|
||||||
|
* Saves persistent NPC Market Data into SQL
|
||||||
|
* @param exname NPC exname
|
||||||
|
* @param nameid Item ID
|
||||||
|
* @param qty Stock
|
||||||
|
**/
|
||||||
|
void npc_market_tosql(const char *exname, struct npc_item_list *list) {
|
||||||
|
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
|
||||||
|
if (SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`name`,`nameid`,`price`,`amount`,`flag`) VALUES ('%s','%hu','%d','%hu','%"PRIu8"')",
|
||||||
|
market_table, exname, list->nameid, list->value, list->qty, list->flag) ||
|
||||||
|
SQL_ERROR == SqlStmt_Execute(stmt))
|
||||||
|
SqlStmt_ShowDebug(stmt);
|
||||||
|
SqlStmt_Free(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes persistent NPC Market Data from SQL
|
||||||
|
* @param exname NPC exname
|
||||||
|
* @param nameid Item ID
|
||||||
|
* @param clear True: will removes all records related with the NPC
|
||||||
|
**/
|
||||||
|
void npc_market_delfromsql_(const char *exname, unsigned short nameid, bool clear) {
|
||||||
|
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
|
||||||
|
if (clear) {
|
||||||
|
if( SQL_ERROR == SqlStmt_Prepare(stmt, "DELETE FROM `%s` WHERE `name`='%s'", market_table, exname) ||
|
||||||
|
SQL_ERROR == SqlStmt_Execute(stmt))
|
||||||
|
SqlStmt_ShowDebug(stmt);
|
||||||
|
} else {
|
||||||
|
if (SQL_ERROR == SqlStmt_Prepare(stmt, "DELETE FROM `%s` WHERE `name`='%s' AND `nameid`='%d' LIMIT 1", market_table, exname, nameid) ||
|
||||||
|
SQL_ERROR == SqlStmt_Execute(stmt))
|
||||||
|
SqlStmt_ShowDebug(stmt);
|
||||||
|
}
|
||||||
|
SqlStmt_Free(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check NPC Market Shop for each entry
|
||||||
|
**/
|
||||||
|
static int npc_market_checkall_sub(DBKey key, DBData *data, va_list ap) {
|
||||||
|
struct s_npc_market *market = (struct s_npc_market *)db_data2ptr(data);
|
||||||
|
struct npc_data *nd = NULL;
|
||||||
|
uint16 i;
|
||||||
|
|
||||||
|
if (!market)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
nd = npc_name2id(market->exname);
|
||||||
|
if (!nd) {
|
||||||
|
ShowInfo("npc_market_checkall_sub: NPC '%s' not found, removing...\n", market->exname);
|
||||||
|
npc_market_clearfromsql(market->exname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (nd->subtype != NPCTYPE_MARKETSHOP || !nd->u.shop.shop_item || !nd->u.shop.count ) {
|
||||||
|
ShowError("npc_market_checkall_sub: NPC '%s' is not proper for market, removing...\n", nd->exname);
|
||||||
|
npc_market_clearfromsql(nd->exname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!market->count || !market->list)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i = 0; i < market->count; i++) {
|
||||||
|
struct npc_item_list *list = &market->list[i];
|
||||||
|
uint16 j;
|
||||||
|
|
||||||
|
if (!list->nameid || !itemdb_exists(list->nameid)) {
|
||||||
|
ShowError("npc_market_checkall_sub: NPC '%s' sells invalid item '%hu', deleting...\n", nd->exname, list->nameid);
|
||||||
|
npc_market_delfromsql(nd->exname, list->nameid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reloading stock from `market` table
|
||||||
|
ARR_FIND(0, nd->u.shop.count, j, nd->u.shop.shop_item[j].nameid == list->nameid);
|
||||||
|
if (j != nd->u.shop.count) {
|
||||||
|
nd->u.shop.shop_item[j].value = list->value;
|
||||||
|
nd->u.shop.shop_item[j].qty = list->qty;
|
||||||
|
nd->u.shop.shop_item[j].flag = list->flag;
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list->flag&1) { // Item added by npcshopitem/npcshopadditem, add new entry
|
||||||
|
RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+1);
|
||||||
|
nd->u.shop.shop_item[j].nameid = list->nameid;
|
||||||
|
nd->u.shop.shop_item[j].value = list->value;
|
||||||
|
nd->u.shop.shop_item[j].qty = list->qty;
|
||||||
|
nd->u.shop.shop_item[j].flag = list->flag;
|
||||||
|
nd->u.shop.count++;
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
||||||
|
}
|
||||||
|
else { // Removing "out-of-date" entry
|
||||||
|
ShowError("npc_market_checkall_sub: NPC '%s' does not sell item %hu (qty %hu), deleting...\n", nd->exname, list->nameid, list->qty);
|
||||||
|
npc_market_delfromsql(nd->exname, list->nameid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear NPC market single entry
|
||||||
|
**/
|
||||||
|
static int npc_market_free(DBKey key, DBData *data, va_list ap) {
|
||||||
|
struct s_npc_market *market = (struct s_npc_market *)db_data2ptr(data);
|
||||||
|
if (!market)
|
||||||
|
return 0;
|
||||||
|
if (market->list) {
|
||||||
|
aFree(market->list);
|
||||||
|
market->list = NULL;
|
||||||
|
}
|
||||||
|
aFree(market);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all existing NPC Market Shop after first loading map-server or after reloading scripts.
|
||||||
|
* Overwrite stocks from NPC by using stock entries from `market` table.
|
||||||
|
**/
|
||||||
|
static void npc_market_checkall(void) {
|
||||||
|
if (!db_size(NPCMarketDB))
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
ShowInfo("Checking '"CL_WHITE"%d"CL_RESET"' NPC Markets...\n", db_size(NPCMarketDB));
|
||||||
|
NPCMarketDB->foreach(NPCMarketDB, npc_market_checkall_sub);
|
||||||
|
ShowStatus("Done checking '"CL_WHITE"%d"CL_RESET"' NPC Markets.\n", db_size(NPCMarketDB));
|
||||||
|
NPCMarketDB->clear(NPCMarketDB, npc_market_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads persistent NPC Market Data from SQL, use the records after NPCs init'd to reuse the stock values.
|
||||||
|
**/
|
||||||
|
static void npc_market_fromsql(void) {
|
||||||
|
uint32 count = 0;
|
||||||
|
|
||||||
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "SELECT `name`,`nameid`,`price`,`amount`,`flag` FROM `%s` ORDER BY `name`", market_table)) {
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (SQL_SUCCESS == Sql_NextRow(mmysql_handle)) {
|
||||||
|
char *data;
|
||||||
|
struct s_npc_market *market;
|
||||||
|
struct npc_item_list list;
|
||||||
|
|
||||||
|
Sql_GetData(mmysql_handle, 0, &data, NULL);
|
||||||
|
|
||||||
|
if (!(market = (struct s_npc_market *)strdb_get(NPCMarketDB,data))) {
|
||||||
|
CREATE(market, struct s_npc_market, 1);
|
||||||
|
market->count = 0;
|
||||||
|
safestrncpy(market->exname, data, strlen(data)+1);
|
||||||
|
strdb_put(NPCMarketDB, market->exname, market);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sql_GetData(mmysql_handle, 1, &data, NULL); list.nameid = atoi(data);
|
||||||
|
Sql_GetData(mmysql_handle, 2, &data, NULL); list.value = atoi(data);
|
||||||
|
Sql_GetData(mmysql_handle, 3, &data, NULL); list.qty = atoi(data);
|
||||||
|
Sql_GetData(mmysql_handle, 4, &data, NULL); list.flag = atoi(data);
|
||||||
|
|
||||||
|
RECREATE(market->list, struct npc_item_list, market->count+1);
|
||||||
|
market->list[market->count++] = list;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
Sql_FreeResult(mmysql_handle);
|
||||||
|
|
||||||
|
ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' entries for '"CL_WHITE"%d"CL_RESET"' NPC Markets from '"CL_WHITE"%s"CL_RESET"' table.\n", count, db_size(NPCMarketDB), market_table);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//Set mapcell CELL_NPC to trigger event later
|
//Set mapcell CELL_NPC to trigger event later
|
||||||
void npc_setcells(struct npc_data* nd)
|
void npc_setcells(struct npc_data* nd)
|
||||||
{
|
{
|
||||||
@ -3928,7 +4216,7 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit)
|
|||||||
|
|
||||||
if( strcasecmp(w2,"warp") == 0 && count > 3 )
|
if( strcasecmp(w2,"warp") == 0 && count > 3 )
|
||||||
p = npc_parse_warp(w1,w2,w3,w4, p, buffer, filepath);
|
p = npc_parse_warp(w1,w2,w3,w4, p, buffer, filepath);
|
||||||
else if( (!strcasecmp(w2,"shop") || !strcasecmp(w2,"cashshop") || !strcasecmp(w2,"itemshop") || !strcasecmp(w2,"pointshop")) && count > 3 )
|
else if( (!strcasecmp(w2,"shop") || !strcasecmp(w2,"cashshop") || !strcasecmp(w2,"itemshop") || !strcasecmp(w2,"pointshop") || !strcasecmp(w2,"marketshop") ) && count > 3 )
|
||||||
p = npc_parse_shop(w1,w2,w3,w4, p, buffer, filepath);
|
p = npc_parse_shop(w1,w2,w3,w4, p, buffer, filepath);
|
||||||
else if( strcasecmp(w2,"script") == 0 && count > 3 ) {
|
else if( strcasecmp(w2,"script") == 0 && count > 3 ) {
|
||||||
if( strcasecmp(w1,"function") == 0 )
|
if( strcasecmp(w1,"function") == 0 )
|
||||||
@ -4057,6 +4345,10 @@ int npc_reload(void) {
|
|||||||
|
|
||||||
//Remove all npcs/mobs. [Skotlex]
|
//Remove all npcs/mobs. [Skotlex]
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
npc_market_fromsql();
|
||||||
|
#endif
|
||||||
|
|
||||||
iter = mapit_geteachiddb();
|
iter = mapit_geteachiddb();
|
||||||
for( bl = (struct block_list*)mapit_first(iter); mapit_exists(iter); bl = (struct block_list*)mapit_next(iter) ) {
|
for( bl = (struct block_list*)mapit_first(iter); mapit_exists(iter); bl = (struct block_list*)mapit_next(iter) ) {
|
||||||
switch(bl->type) {
|
switch(bl->type) {
|
||||||
@ -4130,6 +4422,10 @@ int npc_reload(void) {
|
|||||||
if(!CheckForCharServer()){
|
if(!CheckForCharServer()){
|
||||||
ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
|
ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
npc_market_checkall();
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4170,6 +4466,9 @@ void do_final_npc(void) {
|
|||||||
ev_db->destroy(ev_db, NULL);
|
ev_db->destroy(ev_db, NULL);
|
||||||
npcname_db->destroy(npcname_db, NULL);
|
npcname_db->destroy(npcname_db, NULL);
|
||||||
npc_path_db->destroy(npc_path_db, NULL);
|
npc_path_db->destroy(npc_path_db, NULL);
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
NPCMarketDB->destroy(NPCMarketDB, npc_market_free);
|
||||||
|
#endif
|
||||||
ers_destroy(timer_event_ers);
|
ers_destroy(timer_event_ers);
|
||||||
npc_clearsrcfile();
|
npc_clearsrcfile();
|
||||||
}
|
}
|
||||||
@ -4226,6 +4525,10 @@ void do_init_npc(void){
|
|||||||
ev_db = strdb_alloc((DBOptions)(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA),2*NAME_LENGTH+2+1);
|
ev_db = strdb_alloc((DBOptions)(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA),2*NAME_LENGTH+2+1);
|
||||||
npcname_db = strdb_alloc(DB_OPT_BASE,NAME_LENGTH);
|
npcname_db = strdb_alloc(DB_OPT_BASE,NAME_LENGTH);
|
||||||
npc_path_db = strdb_alloc(DB_OPT_BASE|DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,80);
|
npc_path_db = strdb_alloc(DB_OPT_BASE|DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,80);
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
NPCMarketDB = strdb_alloc(DB_OPT_BASE, NAME_LENGTH+1);
|
||||||
|
npc_market_fromsql();
|
||||||
|
#endif
|
||||||
|
|
||||||
timer_event_ers = ers_new(sizeof(struct timer_event_data),"clif.c::timer_event_ers",ERS_OPT_NONE);
|
timer_event_ers = ers_new(sizeof(struct timer_event_data),"clif.c::timer_event_ers",ERS_OPT_NONE);
|
||||||
|
|
||||||
@ -4248,6 +4551,10 @@ void do_init_npc(void){
|
|||||||
memset(script_event, 0, sizeof(script_event));
|
memset(script_event, 0, sizeof(script_event));
|
||||||
npc_read_event_script();
|
npc_read_event_script();
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
npc_market_checkall();
|
||||||
|
#endif
|
||||||
|
|
||||||
//Debug function to locate all endless loop warps.
|
//Debug function to locate all endless loop warps.
|
||||||
if (battle_config.warp_point_debug)
|
if (battle_config.warp_point_debug)
|
||||||
npc_debug_warps();
|
npc_debug_warps();
|
||||||
|
@ -15,13 +15,26 @@ struct view_data;
|
|||||||
struct npc_timerevent_list {
|
struct npc_timerevent_list {
|
||||||
int timer,pos;
|
int timer,pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct npc_label_list {
|
struct npc_label_list {
|
||||||
char name[NAME_LENGTH];
|
char name[NAME_LENGTH];
|
||||||
int pos;
|
int pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Item list for NPC sell/buy list
|
||||||
struct npc_item_list {
|
struct npc_item_list {
|
||||||
unsigned short nameid;
|
unsigned short nameid;
|
||||||
unsigned int value;
|
unsigned int value;
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
unsigned short qty; ///< Stock counter (Market shop)
|
||||||
|
uint8 flag; ///< 1: Item added by npcshopitem/npcshopadditem, force load! (Market shop)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List of bought/sold item for NPC shops
|
||||||
|
struct s_npc_buy_list {
|
||||||
|
unsigned short qty; ///< Amount of item will be bought
|
||||||
|
unsigned short nameid; ///< ID of item will be bought
|
||||||
};
|
};
|
||||||
|
|
||||||
struct npc_data {
|
struct npc_data {
|
||||||
@ -126,8 +139,8 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd);
|
|||||||
int npc_scriptcont(struct map_session_data* sd, int id, bool closing);
|
int 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);
|
||||||
int npc_buylist(struct map_session_data* sd,int n, unsigned short* item_list);
|
uint8 npc_buylist(struct map_session_data* sd, uint16 n, struct s_npc_buy_list *item_list);
|
||||||
int 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);
|
||||||
void npc_parse_mob2(struct spawn_data* mob);
|
void npc_parse_mob2(struct spawn_data* mob);
|
||||||
bool npc_viewisid(const char * viewid);
|
bool npc_viewisid(const char * viewid);
|
||||||
struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y);
|
struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y);
|
||||||
@ -179,6 +192,11 @@ extern struct npc_data* fake_nd;
|
|||||||
int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, unsigned short* item_list);
|
int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, unsigned short* item_list);
|
||||||
bool npc_shop_discount(enum npc_subtype type, bool discount);
|
bool npc_shop_discount(enum npc_subtype type, bool discount);
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
void npc_market_tosql(const char *exname, struct npc_item_list *list);
|
||||||
|
void npc_market_delfromsql_(const char *exname, unsigned short nameid, bool clear);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SECURE_NPCTIMEOUT
|
#ifdef SECURE_NPCTIMEOUT
|
||||||
int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data);
|
int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data);
|
||||||
#endif
|
#endif
|
||||||
|
158
src/map/script.c
158
src/map/script.c
@ -15628,28 +15628,45 @@ BUILDIN_FUNC(callshop)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
shopname = script_getstr(st, 2);
|
shopname = script_getstr(st, 2);
|
||||||
if( script_hasdata(st,3) )
|
if (script_hasdata(st,3))
|
||||||
flag = script_getnum(st,3);
|
flag = script_getnum(st,3);
|
||||||
nd = npc_name2id(shopname);
|
nd = npc_name2id(shopname);
|
||||||
if( !nd || nd->bl.type != BL_NPC || (nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP) )
|
if( !nd || nd->bl.type != BL_NPC || (nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP) ) {
|
||||||
{
|
|
||||||
ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname);
|
ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname);
|
||||||
script_pushint(st,0);
|
script_pushint(st,0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( nd->subtype == NPCTYPE_SHOP || nd->subtype == NPCTYPE_ITEMSHOP || nd->subtype == NPCTYPE_POINTSHOP )
|
if (nd->subtype == NPCTYPE_SHOP || nd->subtype == NPCTYPE_ITEMSHOP || nd->subtype == NPCTYPE_POINTSHOP) {
|
||||||
{
|
|
||||||
// flag the user as using a valid script call for opening the shop (for floating NPCs)
|
// flag the user as using a valid script call for opening the shop (for floating NPCs)
|
||||||
sd->state.callshop = 1;
|
sd->state.callshop = 1;
|
||||||
|
|
||||||
switch( flag )
|
switch (flag) {
|
||||||
{
|
|
||||||
case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window
|
case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window
|
||||||
case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window
|
case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window
|
||||||
default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu
|
default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
else if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
unsigned short i;
|
||||||
|
|
||||||
|
for (i = 0; i < nd->u.shop.count; i++) {
|
||||||
|
if (nd->u.shop.shop_item[i].qty)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == nd->u.shop.count) {
|
||||||
|
clif_colormes(sd, color_table[COLOR_RED], msg_txt(sd, 534));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd->npc_shopid = nd->bl.id;
|
||||||
|
clif_npc_market_open(sd, nd);
|
||||||
|
script_pushint(st,1);
|
||||||
|
return SCRIPT_CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
clif_cashshop_show(sd, nd);
|
clif_cashshop_show(sd, nd);
|
||||||
|
|
||||||
@ -15664,22 +15681,35 @@ BUILDIN_FUNC(npcshopitem)
|
|||||||
struct npc_data* nd = npc_name2id(npcname);
|
struct npc_data* nd = npc_name2id(npcname);
|
||||||
int n, i;
|
int n, i;
|
||||||
int amount;
|
int amount;
|
||||||
|
uint16 offs = 2;
|
||||||
|
|
||||||
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP ) )
|
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP ) ) { // Not found.
|
||||||
{ //Not found.
|
|
||||||
script_pushint(st,0);
|
script_pushint(st,0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
offs = 3;
|
||||||
|
npc_market_delfromsql_(nd->exname, 0, true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// get the count of new entries
|
// get the count of new entries
|
||||||
amount = (script_lastdata(st)-2)/2;
|
amount = (script_lastdata(st)-2)/offs;
|
||||||
|
|
||||||
// generate new shop item list
|
// generate new shop item list
|
||||||
RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount);
|
RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount);
|
||||||
for( n = 0, i = 3; n < amount; n++, i+=2 )
|
for (n = 0, i = 3; n < amount; n++, i+=offs) {
|
||||||
{
|
|
||||||
nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
|
nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
|
||||||
nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
|
nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
nd->u.shop.shop_item[n].qty = script_getnum(st,i+2);
|
||||||
|
nd->u.shop.shop_item[n].flag = 1;
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[n]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
nd->u.shop.count = n;
|
nd->u.shop.count = n;
|
||||||
|
|
||||||
@ -15693,19 +15723,47 @@ BUILDIN_FUNC(npcshopadditem)
|
|||||||
struct npc_data* nd = npc_name2id(npcname);
|
struct npc_data* nd = npc_name2id(npcname);
|
||||||
int n, i;
|
int n, i;
|
||||||
int amount;
|
int amount;
|
||||||
|
uint16 offs = 2;
|
||||||
|
|
||||||
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP ) )
|
if (!nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP)) { // Not found.
|
||||||
{ //Not found.
|
|
||||||
script_pushint(st,0);
|
script_pushint(st,0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP)
|
||||||
|
offs = 3;
|
||||||
|
|
||||||
// get the count of new entries
|
// get the count of new entries
|
||||||
amount = (script_lastdata(st)-2)/2;
|
amount = (script_lastdata(st)-2)/offs;
|
||||||
|
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
for (n = 0, i = 3; n < amount; n++, i += offs) {
|
||||||
|
uint16 nameid = script_getnum(st,i), j;
|
||||||
|
|
||||||
|
// Check existing entries
|
||||||
|
ARR_FIND(0, nd->u.shop.count, j, nd->u.shop.shop_item[j].nameid == nameid);
|
||||||
|
if (j == nd->u.shop.count) {
|
||||||
|
RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+1);
|
||||||
|
j = nd->u.shop.count;
|
||||||
|
nd->u.shop.shop_item[j].flag = 1;
|
||||||
|
nd->u.shop.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
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].qty = script_getnum(st,i+2);
|
||||||
|
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[j]);
|
||||||
|
}
|
||||||
|
script_pushint(st,1);
|
||||||
|
return SCRIPT_CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// append new items to existing shop item list
|
// append new items to existing shop item list
|
||||||
RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount);
|
RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount);
|
||||||
for( n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=2 )
|
for (n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=offs)
|
||||||
{
|
{
|
||||||
nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
|
nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
|
||||||
nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
|
nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
|
||||||
@ -15724,7 +15782,7 @@ BUILDIN_FUNC(npcshopdelitem)
|
|||||||
int amount;
|
int amount;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP ) ) { // Not found.
|
if (!nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP)) { // Not found.
|
||||||
script_pushint(st,0);
|
script_pushint(st,0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -15738,7 +15796,12 @@ BUILDIN_FUNC(npcshopdelitem)
|
|||||||
|
|
||||||
ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid );
|
ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid );
|
||||||
if( n < size ) {
|
if( n < size ) {
|
||||||
memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n));
|
if (n+1 != size)
|
||||||
|
memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n));
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP)
|
||||||
|
npc_market_delfromsql_(nd->exname, nameid, false);
|
||||||
|
#endif
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15750,7 +15813,10 @@ BUILDIN_FUNC(npcshopdelitem)
|
|||||||
return SCRIPT_CMD_SUCCESS;
|
return SCRIPT_CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sets a script to attach to a shop npc.
|
/**
|
||||||
|
* Sets a script to attach to a shop npc.
|
||||||
|
* npcshopattach "<npc_name>";
|
||||||
|
**/
|
||||||
BUILDIN_FUNC(npcshopattach)
|
BUILDIN_FUNC(npcshopattach)
|
||||||
{
|
{
|
||||||
const char* npcname = script_getstr(st,2);
|
const char* npcname = script_getstr(st,2);
|
||||||
@ -15760,8 +15826,7 @@ BUILDIN_FUNC(npcshopattach)
|
|||||||
if( script_hasdata(st,3) )
|
if( script_hasdata(st,3) )
|
||||||
flag = script_getnum(st,3);
|
flag = script_getnum(st,3);
|
||||||
|
|
||||||
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP ) )
|
if (!nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP)) { // Not found.
|
||||||
{ //Not found.
|
|
||||||
script_pushint(st,0);
|
script_pushint(st,0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -19804,6 +19869,55 @@ BUILDIN_FUNC(mergeitem) {
|
|||||||
return SCRIPT_CMD_SUCCESS;
|
return SCRIPT_CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an entry from NPC shop.
|
||||||
|
* npcshopupdate "<name>",<item_id>,<price>{,<stock>}
|
||||||
|
**/
|
||||||
|
BUILDIN_FUNC(npcshopupdate) {
|
||||||
|
const char* npcname = script_getstr(st, 2);
|
||||||
|
struct npc_data* nd = npc_name2id(npcname);
|
||||||
|
uint16 nameid = script_getnum(st, 3);
|
||||||
|
int price = script_getnum(st, 4);
|
||||||
|
uint16 stock = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if( !nd || ( nd->subtype != NPCTYPE_SHOP && nd->subtype != NPCTYPE_CASHSHOP && nd->subtype != NPCTYPE_ITEMSHOP && nd->subtype != NPCTYPE_POINTSHOP && nd->subtype != NPCTYPE_MARKETSHOP ) ) { // Not found.
|
||||||
|
script_pushint(st,0);
|
||||||
|
return SCRIPT_CMD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nd->u.shop.count) {
|
||||||
|
ShowError("buildin_npcshopupdate: Attempt to update empty shop from '%s'.\n", nd->exname);
|
||||||
|
script_pushint(st,0);
|
||||||
|
return SCRIPT_CMD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
FETCH(5, stock);
|
||||||
|
}
|
||||||
|
else if ((price = cap_value(price, 0, INT_MAX)) == 0) { // Nothing to do here...
|
||||||
|
script_pushint(st,1);
|
||||||
|
return SCRIPT_CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nd->u.shop.count; i++) {
|
||||||
|
if (nd->u.shop.shop_item[i].nameid == nameid) {
|
||||||
|
|
||||||
|
if (price != 0)
|
||||||
|
nd->u.shop.shop_item[i].value = price;
|
||||||
|
#if PACKETVER >= 20131223
|
||||||
|
if (nd->subtype == NPCTYPE_MARKETSHOP) {
|
||||||
|
nd->u.shop.shop_item[i].qty = stock;
|
||||||
|
npc_market_tosql(nd->exname, &nd->u.shop.shop_item[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_pushint(st,1);
|
||||||
|
return SCRIPT_CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
#include "../custom/script.inc"
|
#include "../custom/script.inc"
|
||||||
|
|
||||||
// declarations that were supposed to be exported from npc_chat.c
|
// declarations that were supposed to be exported from npc_chat.c
|
||||||
@ -20354,6 +20468,8 @@ struct script_function buildin_func[] = {
|
|||||||
BUILDIN_DEF(countspiritball,"?"),
|
BUILDIN_DEF(countspiritball,"?"),
|
||||||
BUILDIN_DEF(mergeitem,"?"),
|
BUILDIN_DEF(mergeitem,"?"),
|
||||||
|
|
||||||
|
BUILDIN_DEF(npcshopupdate,"sii?"),
|
||||||
|
|
||||||
#include "../custom/script_def.inc"
|
#include "../custom/script_def.inc"
|
||||||
|
|
||||||
{NULL,NULL,NULL},
|
{NULL,NULL,NULL},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user