Skip to content

Commit

Permalink
Merge pull request #364 from rathena/feature/market_shop
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
cydh committed Mar 27, 2015
2 parents 176f3da + 7d929a0 commit 40c63f4
Show file tree
Hide file tree
Showing 13 changed files with 738 additions and 94 deletions.
1 change: 1 addition & 0 deletions conf/inter_athena.conf
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ mob_skill_db2_db: mob_skill_db2
mapreg_db: mapreg
vending_db: vendings
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_db: no
Expand Down
2 changes: 1 addition & 1 deletion conf/msg_conf/map_msg.conf
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@
532: Shadow Right Accessory,
533: Shadow Left Accessory,

//534: // Free
534: Shop is out of stock! Please come back later.

// 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.
Expand Down
8 changes: 5 additions & 3 deletions db/packet_db.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2310,10 +2310,12 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto]
//0x097E,12 //ZC_UPDATE_RANKING_POINT
0x09B4,6,dull,0 //Cash Shop - Special Tab
0x09CE,102,itemmonster,2
0x09D4,2,dull,0 //npcshopclosed
0x09D4,2,npcshopclosed,0
//NPC Market
0x09D6,-1,dull,0 //npcmarketpurchase
0x09D8,2,dull,0 //npcmarketclosed
0x09D5,-1
0x09D6,-1,npcmarketpurchase,2:4:6
0x09D7,-1
0x09D8,2,npcmarketclosed,0
0x09DF,7

//Add new packets here
Expand Down
23 changes: 21 additions & 2 deletions doc/script_commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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>...}
<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
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
Expand Down Expand Up @@ -6239,7 +6241,10 @@ specified will be for sale.

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!

---------------------------------------

Expand All @@ -6251,7 +6256,10 @@ appear twice on the sell list.

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!

---------------------------------------

Expand Down Expand Up @@ -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.

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>}}}}};
Expand Down
33 changes: 33 additions & 0 deletions sql-files/main.sql
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,10 @@ CREATE TABLE IF NOT EXISTS `storage` (
KEY `account_id` (`account_id`)
) ENGINE=MyISAM;

--
-- Table structure for table `interreg`
--

CREATE TABLE IF NOT EXISTS `interreg` (
`varname` varchar(11) NOT NULL,
`value` varchar(20) NOT NULL,
Expand All @@ -714,6 +718,10 @@ CREATE TABLE IF NOT EXISTS `bonus_script` (
`icon` SMALLINT(3) NOT NULL DEFAULT '-1'
) ENGINE=InnoDB;

--
-- Table structure for table `vending_items`
--

CREATE TABLE IF NOT EXISTS `vending_items` (
`vending_id` int(10) unsigned NOT NULL,
`index` smallint(5) unsigned NOT NULL,
Expand All @@ -722,6 +730,10 @@ CREATE TABLE IF NOT EXISTS `vending_items` (
`price` int(10) unsigned NOT NULL
) ENGINE=MyISAM;

--
-- Table structure for table `vendings`
--

CREATE TABLE IF NOT EXISTS `vendings` (
`id` int(10) unsigned NOT NULL,
`account_id` int(11) unsigned NOT NULL,
Expand All @@ -738,6 +750,10 @@ CREATE TABLE IF NOT EXISTS `vendings` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM;

--
-- Table structure for table `buyingstore_items`
--

CREATE TABLE IF NOT EXISTS `buyingstore_items` (
`buyingstore_id` int(10) unsigned NOT NULL,
`index` smallint(5) unsigned NOT NULL,
Expand All @@ -746,6 +762,10 @@ CREATE TABLE IF NOT EXISTS `buyingstore_items` (
`price` int(10) unsigned NOT NULL
) ENGINE=MyISAM;

--
-- Table structure for table `buyingstores`
--

CREATE TABLE IF NOT EXISTS `buyingstores` (
`id` int(10) unsigned NOT NULL,
`account_id` int(11) unsigned NOT NULL,
Expand All @@ -762,3 +782,16 @@ CREATE TABLE IF NOT EXISTS `buyingstores` (
`autotrade` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) 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 changes: 12 additions & 0 deletions sql-files/upgrades/upgrade_20150327_market.sql
Original file line number Diff line number Diff line change
@@ -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 changes: 133 additions & 5 deletions src/map/clif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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).
/// 00b4 <packet len>.W <npc id>.L <message>.?B
/// Client behavior (dialog window):
Expand Down Expand Up @@ -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)
{
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
int n = (RFIFOW(fd,info->pos[0])-4) /4;
unsigned short* item_list = (unsigned short*)RFIFOP(fd,info->pos[1]);
uint16 n = (RFIFOW(fd,info->pos[0])-4) /4;
int result;

if( sd->state.trading || !sd->npc_shopid )
result = 1;
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.

clif_npc_buy_result(sd, result);
}

Expand Down Expand Up @@ -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,
//#0x09C0
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,
};
Expand Down Expand Up @@ -18214,6 +18339,9 @@ void packetdb_readdb(bool reload)
{ clif_parse_client_version, "clientversion"},
{ clif_parse_blocking_playcancel, "booking_playcancel"},
{ clif_parse_ranklist, "ranklist"},
/* Market NPC */
{ clif_parse_NPCMarketClosed, "npcmarketclosed" },
{ clif_parse_NPCMarketPurchase, "npcmarketpurchase" },
{NULL,NULL}
};
struct {
Expand Down
3 changes: 3 additions & 0 deletions src/map/clif.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_buylist(struct map_session_data *sd, struct npc_data *nd); //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_scriptnext(struct map_session_data *sd,int npcid); //self
void clif_scriptclose(struct map_session_data *sd, int npcid); //self
Expand Down
3 changes: 3 additions & 0 deletions src/map/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 vendings_db[32] = "vendings";
char vending_items_db[32] = "vending_items";
char market_table[32] = "market";

// log database
char log_db_ip[32] = "127.0.0.1";
Expand Down Expand Up @@ -3759,6 +3760,8 @@ int inter_config_read(char *cfgName)
strcpy( vendings_db, w2 );
else if( strcmpi( w1, "vending_items_db" ) == 0 )
strcpy( vending_items_db, w2 );
else if (strcmpi(w1, "market_table") == 0)
strcpy(market_table, w2);
else
//Map Server SQL DB
if(strcmpi(w1,"map_server_ip")==0)
Expand Down
4 changes: 3 additions & 1 deletion src/map/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@ enum npc_subtype {
NPCTYPE_CASHSHOP, /// Cashshop
NPCTYPE_ITEMSHOP, /// Itemshop
NPCTYPE_POINTSHOP, /// Pointshop
NPCTYPE_TOMB /// Monster tomb
NPCTYPE_TOMB, /// Monster tomb
NPCTYPE_MARKETSHOP, /// Marketshop
};

enum e_race {
Expand Down Expand Up @@ -993,6 +994,7 @@ extern char mob_skill_db_re_db[32];
extern char mob_skill_db2_db[32];
extern char vendings_db[32];
extern char vending_items_db[32];
extern char market_table[32];

void do_shutdown(void);

Expand Down

0 comments on commit 40c63f4

Please sign in to comment.