* Implemented search store info system (aka. vending and buying store search) together with related items.
- Requires 2010-08-03aRagexeRE or later and can be disabled in 'conf/battle/feature.conf'. git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@14732 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
parent
ffe2f08b20
commit
6b74ef9b50
@ -1,5 +1,8 @@
|
||||
Date Added
|
||||
|
||||
2011/03/06
|
||||
* Implemented search store info system (aka. vending and buying store search) together with related items. [Ai4rei]
|
||||
- Requires 2010-08-03aRagexeRE or later and can be disabled in 'conf/battle/feature.conf'.
|
||||
2011/03/05
|
||||
* Fixed possible crash in script_reportdata, when a script string becomes NULL for whatever reason. [Ai4rei]
|
||||
2011/03/04
|
||||
|
@ -1,5 +1,7 @@
|
||||
Date Added
|
||||
|
||||
2011/03/06
|
||||
* Rev. 14732 Added search store info related settings. [Ai4rei]
|
||||
2011/02/23
|
||||
* Rev. 14724 Made autotrade error message store type-neutral, as it is used for buying stores now as well. [Ai4rei]
|
||||
2011/02/19
|
||||
|
@ -20,5 +20,9 @@
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// Buying store (Note 1)
|
||||
// Requires: 2010-04-20aRagexeRE or later
|
||||
// Requires: 2010-04-27aRagexeRE or later
|
||||
feature.buying_store: on
|
||||
|
||||
// Search stores (Note 1)
|
||||
// Requires: 2010-08-03aRagexeRE or later
|
||||
feature.search_stores: on
|
||||
|
@ -121,3 +121,10 @@ auction_feeperhour: 12000
|
||||
|
||||
// Auction maximum sell price
|
||||
auction_maximumprice: 500000000
|
||||
|
||||
// Minimum delay between each store search query in seconds.
|
||||
searchstore_querydelay: 10
|
||||
|
||||
// Maximum amount of results a store search query may yield, before
|
||||
// it is canceled.
|
||||
searchstore_maxresults: 30
|
||||
|
@ -9,6 +9,9 @@
|
||||
13005 Angelic Wing Dagger: NEED INFO.
|
||||
=======================
|
||||
|
||||
2011/03/06
|
||||
* Rev. 14732 Added Universal Catalog Silver, Gold and Bronze and their respective boxes. [Ai4rei]
|
||||
- Updated packet info related to search store info.
|
||||
2011/02/19
|
||||
* Rev. 14713 Database updates required by buying store system implementation. [Ai4rei]
|
||||
- Added database of items, that can be sold to a buying store (item_buyingstore.txt).
|
||||
|
@ -4830,6 +4830,9 @@
|
||||
//12556,Sleipnir_Piece_Box,
|
||||
//12557,Mjolnir_Piece_Box,
|
||||
//12558,Megingiorde_Piece_Box,
|
||||
12580,Universal_Catalog_Silver,Universal Catalog Silver,2,200,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,0; },{},{}
|
||||
12581,Universal_Catalog_Gold,Universal Catalog Gold,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,1; },{},{}
|
||||
12591,Universal_Catalog_Bronze,Universal Catalog Bronze,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,1; },{},{}
|
||||
12701,Old_Blue_Box_F,Old Blue Box,2,,,200,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
|
||||
12702,Old_Bleu_Box,Old Navy Box,2,0,,200,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem groupranditem(IG_BleuBox),1; getitem groupranditem(IG_BleuBox),1; },{},{}
|
||||
12703,Holy_Egg_2,Holy Egg,11,0,,50,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
|
||||
@ -6183,6 +6186,12 @@
|
||||
//16588,Thoughtful_Hat_Box
|
||||
//16589,Thoughtful_Hat_Box
|
||||
//16590,Thoughtful_Hat_Box
|
||||
16677,Universal_Catalog_Gold_Box10,Universal Catalog Gold 10 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,10; },{},{}
|
||||
16678,Universal_Catalog_Gold_Box50,Universal Catalog Gold 50 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,50; },{},{}
|
||||
16679,Universal_Catalog_Gold_Box10,Universal Catalog Gold 10 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,10; },{},{}
|
||||
16680,Universal_Catalog_Gold_Box50,Universal Catalog Gold 50 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,50; },{},{}
|
||||
16776,Universal_Catalog_Gold_Box10,Universal Catalog Gold 10 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,10; },{},{}
|
||||
16777,Universal_Catalog_Gold_Box50,Universal Catalog Gold 50 Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12581,50; },{},{}
|
||||
//18000,Cannon_Ball
|
||||
//18001,Holy_Cannon_Ball
|
||||
//18002,Dark_Cannon_Ball
|
||||
|
@ -1541,17 +1541,17 @@ packet_ver: 25
|
||||
//2010-06-01aRagexeRE
|
||||
//0x0825,-1
|
||||
//0x0826,4
|
||||
//0x0835,-1
|
||||
//0x0836,-1
|
||||
//0x0837,3
|
||||
0x0835,-1,searchstoreinfo,2:4:5:9:13:14:15
|
||||
0x0836,-1
|
||||
0x0837,3
|
||||
//0x0838,3
|
||||
|
||||
//2010-06-08aRagexeRE
|
||||
//0x0838,2
|
||||
//0x083A,4 // Search Stalls Feature
|
||||
//0x083B,2
|
||||
//0x083C,12
|
||||
//0x083D,6
|
||||
0x0838,2,searchstoreinfonextpage,0
|
||||
0x083A,4 // Search Stalls Feature
|
||||
0x083B,2,closesearchstoreinfo,0
|
||||
0x083C,12,searchstoreinfolistitemclick,2:6:10
|
||||
0x083D,6
|
||||
|
||||
//2010-06-15aRagexeRE
|
||||
//0x083E,26
|
||||
@ -1566,7 +1566,7 @@ packet_ver: 25
|
||||
//0x07F3,6
|
||||
|
||||
//2010-07-01aRagexeRE
|
||||
//0x083A,5 // Search Stalls Feature
|
||||
0x083A,5 // Search Stalls Feature
|
||||
|
||||
//2010-07-13aRagexeRE
|
||||
//0x0827,6
|
||||
|
@ -4,7 +4,7 @@
|
||||
//= A reference manual for the eAthena scripting language.
|
||||
//= Commands are sorted depending on their functionality.
|
||||
//===== Version ===========================================
|
||||
//= 3.36.20110219
|
||||
//= 3.37.20110306
|
||||
//=========================================================
|
||||
//= 1.0 - First release, filled will as much info as I could
|
||||
//= remember or figure out, most likely there are errors,
|
||||
@ -157,6 +157,8 @@
|
||||
//= Removed bug warning from 'deletearray'. [Ai4rei]
|
||||
//= 3.36.20110219
|
||||
//= Added 'buyingstore' command. [Ai4rei]
|
||||
//= 3.37.20110306
|
||||
//= Added 'searchstores' command. [Ai4rei]
|
||||
//=========================================================
|
||||
|
||||
This document is a reference manual for all the scripting commands and functions
|
||||
@ -4438,6 +4440,26 @@ Example:
|
||||
// Gives the player oppurtunity to buy 4 different kinds of items.
|
||||
buyingstore 4;
|
||||
|
||||
---------------------------------------
|
||||
|
||||
*searchstores <uses>,<effect>;
|
||||
|
||||
Invokes the store search window, which allows to search for both vending
|
||||
and buying stores. Parameter uses indicates, how many searches can be
|
||||
started, before the window has to be reopened. Effect value affects,
|
||||
what happens, when a result item is double-clicked and can be one of the
|
||||
following:
|
||||
|
||||
0 = Shows the store's position on the mini-map and highlights the
|
||||
shop sign with yellow color, when the store is on same map
|
||||
as the invoking player.
|
||||
1 = Directly opens the shop, regardless of distance.
|
||||
|
||||
Example:
|
||||
|
||||
// Item Universal_Catalog_Gold (10 uses, effect: open shop)
|
||||
searchstores 10,1;
|
||||
|
||||
---------------------------------------
|
||||
//
|
||||
4,1.- End of item-related commands
|
||||
|
@ -18,7 +18,7 @@ MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \
|
||||
storage.o skill.o atcommand.o battle.o battleground.o \
|
||||
intif.o trade.o party.o vending.o guild.o pet.o \
|
||||
log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \
|
||||
buyingstore.o
|
||||
buyingstore.o searchstore.o
|
||||
MAP_TXT_OBJ = $(MAP_OBJ:%=obj_txt/%) \
|
||||
obj_txt/mapreg_txt.o
|
||||
MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \
|
||||
@ -28,7 +28,7 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
|
||||
storage.h skill.h atcommand.h battle.h battleground.h \
|
||||
intif.h trade.h party.h vending.h guild.h pet.h \
|
||||
log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \
|
||||
buyingstore.h
|
||||
buyingstore.h searchstore.h
|
||||
|
||||
HAVE_MYSQL=@HAVE_MYSQL@
|
||||
ifeq ($(HAVE_MYSQL),yes)
|
||||
|
@ -4008,6 +4008,9 @@ static const struct _battle_data {
|
||||
{ "client_sort_storage", &battle_config.client_sort_storage, 0, 0, 1, },
|
||||
{ "gm_check_minlevel", &battle_config.gm_check_minlevel, 60, 0, 100, },
|
||||
{ "feature.buying_store", &battle_config.feature_buying_store, 1, 0, 1, },
|
||||
{ "feature.search_stores", &battle_config.feature_search_stores, 1, 0, 1, },
|
||||
{ "searchstore_querydelay", &battle_config.searchstore_querydelay, 10, 0, INT_MAX, },
|
||||
{ "searchstore_maxresults", &battle_config.searchstore_maxresults, 30, 1, INT_MAX, },
|
||||
// BattleGround Settings
|
||||
{ "bg_update_interval", &battle_config.bg_update_interval, 1000, 100, INT_MAX, },
|
||||
{ "bg_short_attack_damage_rate", &battle_config.bg_short_damage_rate, 80, 0, INT_MAX, },
|
||||
|
@ -482,6 +482,9 @@ extern struct Battle_Config
|
||||
int client_sort_storage;
|
||||
int gm_check_minlevel; // min GM level for /check
|
||||
int feature_buying_store;
|
||||
int feature_search_stores;
|
||||
int searchstore_querydelay;
|
||||
int searchstore_maxresults;
|
||||
|
||||
// [BattleGround Settings]
|
||||
int bg_update_interval;
|
||||
|
@ -34,6 +34,7 @@ enum e_buyingstore_failure
|
||||
|
||||
|
||||
static unsigned int buyingstore_nextid = 0;
|
||||
static short buyingstore_blankslots[MAX_SLOTS] = { 0 }; // used when checking whether or not an item's card slots are blank
|
||||
|
||||
|
||||
/// Returns unique buying store id
|
||||
@ -217,7 +218,7 @@ void buyingstore_open(struct map_session_data* sd, int account_id)
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) )
|
||||
if( !searchstore_queryremote(sd, account_id) && ( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) ) )
|
||||
{// out of view range
|
||||
return;
|
||||
}
|
||||
@ -229,7 +230,6 @@ void buyingstore_open(struct map_session_data* sd, int account_id)
|
||||
|
||||
void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count)
|
||||
{
|
||||
short blankslots[MAX_SLOTS]; // used when checking whether or not an item's card slots are blank
|
||||
int zeny = 0;
|
||||
unsigned int i, weight, listidx, k;
|
||||
struct map_session_data* pl_sd;
|
||||
@ -258,18 +258,19 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) )
|
||||
if( !searchstore_queryremote(sd, account_id) && ( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) ) )
|
||||
{// out of view range
|
||||
clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
searchstore_clearremote(sd);
|
||||
|
||||
if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit )
|
||||
{// buyer lost zeny in the mean time? fix the limit
|
||||
pl_sd->buyingstore.zenylimit = pl_sd->status.zeny;
|
||||
}
|
||||
weight = pl_sd->weight;
|
||||
memset(blankslots, 0, sizeof(blankslots));
|
||||
|
||||
// check item list
|
||||
for( i = 0; i < count; i++ )
|
||||
@ -299,7 +300,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, blankslots, sizeof(blankslots)) )
|
||||
if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) )
|
||||
{// non-tradable item
|
||||
clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
|
||||
return;
|
||||
@ -401,3 +402,69 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int
|
||||
map_quit(pl_sd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Checks if an item is being bought in given player's buying store.
|
||||
bool buyingstore_search(struct map_session_data* sd, unsigned short nameid)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if( !sd->state.buyingstore )
|
||||
{// not buying
|
||||
return false;
|
||||
}
|
||||
|
||||
ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == nameid && sd->buyingstore.items[i].amount );
|
||||
if( i == sd->buyingstore.slots )
|
||||
{// not found
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Searches for all items in a buyingstore, that match given ids, price and possible cards.
|
||||
/// @return Whether or not the search should be continued.
|
||||
bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s)
|
||||
{
|
||||
unsigned int i, idx;
|
||||
struct s_buyingstore_item* it;
|
||||
|
||||
if( !sd->state.buyingstore )
|
||||
{// not buying
|
||||
return true;
|
||||
}
|
||||
|
||||
for( idx = 0; idx < s->item_count; idx++ )
|
||||
{
|
||||
ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == s->itemlist[idx] && sd->buyingstore.items[i].amount );
|
||||
if( i == sd->buyingstore.slots )
|
||||
{// not found
|
||||
continue;
|
||||
}
|
||||
it = &sd->buyingstore.items[i];
|
||||
|
||||
if( s->min_price && s->min_price > (unsigned int)it->price )
|
||||
{// too low price
|
||||
continue;
|
||||
}
|
||||
|
||||
if( s->max_price && s->max_price < (unsigned int)it->price )
|
||||
{// too high price
|
||||
continue;
|
||||
}
|
||||
|
||||
if( s->card_count )
|
||||
{// ignore cards, as there cannot be any
|
||||
;
|
||||
}
|
||||
|
||||
if( !searchstore_result(s->search_sd, sd->buyer_id, sd->status.account_id, sd->message, it->nameid, it->amount, it->price, buyingstore_blankslots, 0) )
|
||||
{// result set full
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#ifndef _BUYINGSTORE_H_
|
||||
#define _BUYINGSTORE_H_
|
||||
|
||||
struct s_search_store_search;
|
||||
|
||||
#define MAX_BUYINGSTORE_SLOTS 5
|
||||
|
||||
struct s_buyingstore_item
|
||||
@ -25,5 +27,7 @@ void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
|
||||
void buyingstore_close(struct map_session_data* sd);
|
||||
void buyingstore_open(struct map_session_data* sd, int account_id);
|
||||
void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count);
|
||||
bool buyingstore_search(struct map_session_data* sd, unsigned short nameid);
|
||||
bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s);
|
||||
|
||||
#endif // _BUYINGSTORE_H_
|
||||
|
199
src/map/clif.c
199
src/map/clif.c
@ -14323,6 +14323,198 @@ void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short res
|
||||
}
|
||||
|
||||
|
||||
/// Search Store Info System
|
||||
///
|
||||
|
||||
/// Request to search for stores (CZ_SEARCH_STORE_INFO)
|
||||
/// 0835 <packet len>.W <type>.B <max price>.L <min price>.L <name id count>.B <card count>.B { <name id>.W }* { <card>.W }*
|
||||
/// type:
|
||||
/// 0 = Vending
|
||||
/// 1 = Buying Store
|
||||
///
|
||||
/// @note The client determines the item ids by specifying a name and optionally,
|
||||
/// amount of card slots. If the client does not know about the item it
|
||||
/// cannot be searched.
|
||||
static void clif_parse_SearchStoreInfo(int fd, struct map_session_data* sd)
|
||||
{
|
||||
const unsigned int blocksize = 2;
|
||||
const uint8* itemlist;
|
||||
const uint8* cardlist;
|
||||
unsigned char type;
|
||||
unsigned int min_price, max_price, packet_len, count, item_count, card_count;
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
|
||||
packet_len = RFIFOW(fd,info->pos[0]);
|
||||
|
||||
if( packet_len < 15 )
|
||||
{// minimum packet length
|
||||
ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 15, packet_len, sd->bl.id);
|
||||
return;
|
||||
}
|
||||
|
||||
type = RFIFOB(fd,info->pos[1]);
|
||||
max_price = RFIFOL(fd,info->pos[2]);
|
||||
min_price = RFIFOL(fd,info->pos[3]);
|
||||
item_count = RFIFOB(fd,info->pos[4]);
|
||||
card_count = RFIFOB(fd,info->pos[5]);
|
||||
itemlist = RFIFOP(fd,info->pos[6]);
|
||||
cardlist = RFIFOP(fd,info->pos[6]+blocksize*item_count);
|
||||
|
||||
// check, if there is enough data for the claimed count of items
|
||||
packet_len-= info->pos[6];
|
||||
|
||||
if( packet_len%blocksize )
|
||||
{
|
||||
ShowError("clif_parse_SearchStoreInfo: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
|
||||
return;
|
||||
}
|
||||
count = packet_len/blocksize;
|
||||
|
||||
if( count < item_count+card_count )
|
||||
{
|
||||
ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected count=%u, count=%u, account_id=%d).\n", item_count+card_count, count, sd->bl.id);
|
||||
return;
|
||||
}
|
||||
|
||||
searchstore_query(sd, type, min_price, max_price, (const unsigned short*)itemlist, item_count, (const unsigned short*)cardlist, card_count);
|
||||
}
|
||||
|
||||
|
||||
/// Results for a store search request (ZC_SEARCH_STORE_INFO_ACK)
|
||||
/// 0836 <packet len>.W <is first page>.B <is next page>.B <remaining uses>.B { <store id>.L <account id>.L <shop name>.80B <nameid>.W <item type>.B <price>.L <amount>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
|
||||
/// is first page:
|
||||
/// 0 = appends to existing results
|
||||
/// 1 = clears previous results before displaying this result set
|
||||
/// is next page:
|
||||
/// 0 = no "next" label
|
||||
/// 1 = "next" label to retrieve more results
|
||||
void clif_search_store_info_ack(struct map_session_data* sd)
|
||||
{
|
||||
const unsigned int blocksize = MESSAGE_SIZE+26;
|
||||
int fd = sd->fd;
|
||||
unsigned int i, start, end;
|
||||
|
||||
start = sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE;
|
||||
end = min(sd->searchstore.count, start+SEARCHSTORE_RESULTS_PER_PAGE);
|
||||
|
||||
WFIFOHEAD(fd,7+(end-start)*blocksize);
|
||||
WFIFOW(fd,0) = 0x836;
|
||||
WFIFOW(fd,2) = 7+(end-start)*blocksize;
|
||||
WFIFOB(fd,4) = !sd->searchstore.pages;
|
||||
WFIFOB(fd,5) = searchstore_querynext(sd);
|
||||
WFIFOB(fd,6) = (unsigned char)min(sd->searchstore.uses, UCHAR_MAX);
|
||||
|
||||
for( i = start; i < end; i++ )
|
||||
{
|
||||
struct s_search_store_info_item* ssitem = &sd->searchstore.items[i];
|
||||
struct item it;
|
||||
|
||||
WFIFOL(fd,i*blocksize+ 7) = ssitem->store_id;
|
||||
WFIFOL(fd,i*blocksize+11) = ssitem->account_id;
|
||||
memcpy(WFIFOP(fd,i*blocksize+15), ssitem->store_name, MESSAGE_SIZE);
|
||||
WFIFOW(fd,i*blocksize+15+MESSAGE_SIZE) = ssitem->nameid;
|
||||
WFIFOB(fd,i*blocksize+17+MESSAGE_SIZE) = itemtype(itemdb_type(ssitem->nameid));
|
||||
WFIFOL(fd,i*blocksize+18+MESSAGE_SIZE) = ssitem->price;
|
||||
WFIFOW(fd,i*blocksize+22+MESSAGE_SIZE) = ssitem->amount;
|
||||
WFIFOB(fd,i*blocksize+24+MESSAGE_SIZE) = ssitem->refine;
|
||||
|
||||
// make-up an item for clif_addcards
|
||||
memset(&it, 0, sizeof(it));
|
||||
memcpy(&it.card, &ssitem->card, sizeof(it.card));
|
||||
it.nameid = ssitem->nameid;
|
||||
it.amount = ssitem->amount;
|
||||
|
||||
clif_addcards(WFIFOP(fd,i*blocksize+25+MESSAGE_SIZE), &it);
|
||||
}
|
||||
|
||||
WFIFOSET(fd,WFIFOW(fd,2));
|
||||
}
|
||||
|
||||
|
||||
/// Notification of failure when searching for stores (ZC_SEARCH_STORE_INFO_FAILED)
|
||||
/// 0837 <reason>.B
|
||||
/// reason:
|
||||
/// 0 = "No matching stores were found." (0x70b)
|
||||
/// 1 = "There are too many results. Please enter more detailed search term." (0x6f8)
|
||||
/// 2 = "You cannot search anymore." (0x706)
|
||||
/// 3 = "You cannot search yet." (0x708)
|
||||
/// 4 = "No sale (purchase) information available." (0x705)
|
||||
void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
WFIFOHEAD(fd,packet_len(0x837));
|
||||
WFIFOW(fd,0) = 0x837;
|
||||
WFIFOB(fd,2) = reason;
|
||||
WFIFOSET(fd,packet_len(0x837));
|
||||
}
|
||||
|
||||
|
||||
/// Request to display next page of results (CZ_SEARCH_STORE_INFO_NEXT_PAGE)
|
||||
/// 0838
|
||||
static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data* sd)
|
||||
{
|
||||
searchstore_next(sd);
|
||||
}
|
||||
|
||||
|
||||
/// Opens the search store window (ZC_OPEN_SEARCH_STORE_INFO)
|
||||
/// 083a <type>.W <remaining uses>.B
|
||||
/// type:
|
||||
/// 0 = Search Stores
|
||||
/// 1 = Search Stores (Cash), asks for confirmation, when clicking a store
|
||||
void clif_open_search_store_info(struct map_session_data* sd)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
WFIFOHEAD(fd,packet_len(0x83a));
|
||||
WFIFOW(fd,0) = 0x83a;
|
||||
WFIFOW(fd,2) = sd->searchstore.effect;
|
||||
#if PACKETVER > 20100701
|
||||
WFIFOB(fd,4) = (unsigned char)min(sd->searchstore.uses, UCHAR_MAX);
|
||||
#endif
|
||||
WFIFOSET(fd,packet_len(0x83a));
|
||||
}
|
||||
|
||||
|
||||
/// Request to close the store search window (CZ_CLOSE_SEARCH_STORE_INFO)
|
||||
/// 083b
|
||||
static void clif_parse_CloseSearchStoreInfo(int fd, struct map_session_data* sd)
|
||||
{
|
||||
searchstore_close(sd);
|
||||
}
|
||||
|
||||
|
||||
/// Request to invoke catalog effect on a store from search results (CZ_SSILIST_ITEM_CLICK)
|
||||
/// 083c <account id>.L <store id>.L <nameid>.W
|
||||
static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data* sd)
|
||||
{
|
||||
unsigned short nameid;
|
||||
int account_id, store_id;
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
|
||||
account_id = RFIFOL(fd,info->pos[0]);
|
||||
store_id = RFIFOL(fd,info->pos[1]);
|
||||
nameid = RFIFOW(fd,info->pos[2]);
|
||||
|
||||
searchstore_click(sd, account_id, store_id, nameid);
|
||||
}
|
||||
|
||||
|
||||
/// Notification of the store position on current map (ZC_SSILIST_ITEM_CLICK_ACK)
|
||||
/// 083d <xPos>.W <yPos>.W
|
||||
void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
WFIFOHEAD(fd,packet_len(0x83d));
|
||||
WFIFOW(fd,0) = 0x83d;
|
||||
WFIFOW(fd,2) = x;
|
||||
WFIFOW(fd,4) = y;
|
||||
WFIFOSET(fd,packet_len(0x83d));
|
||||
}
|
||||
|
||||
|
||||
/*==========================================
|
||||
* ƒpƒPƒbƒgƒfƒoƒbƒO
|
||||
*------------------------------------------*/
|
||||
@ -14727,7 +14919,7 @@ static int packetdb_readdb(void)
|
||||
#endif
|
||||
3, -1, 8, -1, 86, 2, 6, 6, -1, -1, 4, 10, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, -1, -1, 3, 2, 66, 5, 2, 12, 6, 0, 0,
|
||||
};
|
||||
struct {
|
||||
void (*func)(int, struct map_session_data *);
|
||||
@ -14923,6 +15115,11 @@ static int packetdb_readdb(void)
|
||||
{clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"},
|
||||
{clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"},
|
||||
{clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"},
|
||||
// Store Search
|
||||
{clif_parse_SearchStoreInfo,"searchstoreinfo"},
|
||||
{clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"},
|
||||
{clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"},
|
||||
{clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
|
@ -619,4 +619,10 @@ void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short na
|
||||
void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price);
|
||||
void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid);
|
||||
|
||||
/// Search Store System
|
||||
void clif_search_store_info_ack(struct map_session_data* sd);
|
||||
void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason);
|
||||
void clif_open_search_store_info(struct map_session_data* sd);
|
||||
void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y);
|
||||
|
||||
#endif /* _CLIF_H_ */
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "map.h" // RC_MAX
|
||||
#include "pc.h" // struct map_session_data
|
||||
#include "script.h" // struct script_reg, struct script_regstr
|
||||
#include "searchstore.h" // struct s_search_store_info
|
||||
#include "status.h" // OPTION_*, struct weapon_atk
|
||||
#include "unit.h" // unit_stop_attack(), unit_stop_walking()
|
||||
#include "vending.h" // struct s_vending
|
||||
@ -360,6 +361,8 @@ struct map_session_data {
|
||||
unsigned int buyer_id; // uid of open buying store
|
||||
struct s_buyingstore buyingstore;
|
||||
|
||||
struct s_search_store_info searchstore;
|
||||
|
||||
struct pet_data *pd;
|
||||
struct homun_data *hd; // [blackhole89]
|
||||
struct mercenary_data *md;
|
||||
|
@ -14831,6 +14831,39 @@ BUILDIN_FUNC(buyingstore)
|
||||
}
|
||||
|
||||
|
||||
/// Invokes search store info window
|
||||
/// searchstores <uses>,<effect>;
|
||||
BUILDIN_FUNC(searchstores)
|
||||
{
|
||||
unsigned short effect;
|
||||
unsigned int uses;
|
||||
struct map_session_data* sd;
|
||||
|
||||
if( ( sd = script_rid2sd(st) ) == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uses = script_getnum(st,2);
|
||||
effect = script_getnum(st,3);
|
||||
|
||||
if( !uses )
|
||||
{
|
||||
ShowError("buildin_searchstores: Amount of uses cannot be zero.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( effect > 1 )
|
||||
{
|
||||
ShowError("buildin_searchstores: Invalid effect id %hu, specified.\n", effect);
|
||||
return 1;
|
||||
}
|
||||
|
||||
searchstore_open(sd, uses, effect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// declarations that were supposed to be exported from npc_chat.c
|
||||
#ifdef PCRE_SUPPORT
|
||||
BUILDIN_FUNC(defpattern);
|
||||
@ -15193,6 +15226,7 @@ struct script_function buildin_func[] = {
|
||||
BUILDIN_DEF(progressbar,"si"),
|
||||
BUILDIN_DEF(pushpc,"ii"),
|
||||
BUILDIN_DEF(buyingstore,"i"),
|
||||
BUILDIN_DEF(searchstores,"ii"),
|
||||
// WoE SE
|
||||
BUILDIN_DEF(agitstart2,""),
|
||||
BUILDIN_DEF(agitend2,""),
|
||||
|
406
src/map/searchstore.c
Normal file
406
src/map/searchstore.c
Normal file
@ -0,0 +1,406 @@
|
||||
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/malloc.h" // aMalloc, aRealloc, aFree
|
||||
#include "../common/showmsg.h" // ShowError, ShowWarning
|
||||
#include "../common/strlib.h" // safestrncpy
|
||||
#include "atcommand.h" // msg_txt
|
||||
#include "battle.h" // battle_config.*
|
||||
#include "clif.h" // clif_open_search_store_info, clif_search_store_info_*
|
||||
#include "pc.h" // struct map_session_data, pc_setpos, pc_isGM
|
||||
#include "searchstore.h" // struct s_search_store_info
|
||||
|
||||
|
||||
/// failure constants for clif functions
|
||||
enum e_searchstore_failure
|
||||
{
|
||||
SSI_FAILED_NOTHING_SEARCH_ITEM = 0, // "No matching stores were found."
|
||||
SSI_FAILED_OVER_MAXCOUNT = 1, // "There are too many results. Please enter more detailed search term."
|
||||
SSI_FAILED_SEARCH_CNT = 2, // "You cannot search anymore."
|
||||
SSI_FAILED_LIMIT_SEARCH_TIME = 3, // "You cannot search yet."
|
||||
SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE = 4, // "No sale (purchase) information available."
|
||||
};
|
||||
|
||||
|
||||
enum e_searchstore_searchtype
|
||||
{
|
||||
SEARCHTYPE_VENDING = 0,
|
||||
SEARCHTYPE_BUYING_STORE = 1,
|
||||
};
|
||||
|
||||
|
||||
enum e_searchstore_effecttype
|
||||
{
|
||||
EFFECTTYPE_NORMAL = 0,
|
||||
EFFECTTYPE_CASH = 1,
|
||||
EFFECTTYPE_MAX
|
||||
};
|
||||
|
||||
|
||||
/// type for shop search function
|
||||
typedef bool (*searchstore_search_t)(struct map_session_data* sd, unsigned short nameid);
|
||||
typedef bool (*searchstore_searchall_t)(struct map_session_data* sd, const struct s_search_store_search* s);
|
||||
|
||||
|
||||
/// retrieves search function by type
|
||||
static searchstore_search_t searchstore_getsearchfunc(unsigned char type)
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case SEARCHTYPE_VENDING: return &vending_search;
|
||||
case SEARCHTYPE_BUYING_STORE: return &buyingstore_search;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// retrieves search-all function by type
|
||||
static searchstore_searchall_t searchstore_getsearchallfunc(unsigned char type)
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case SEARCHTYPE_VENDING: return &vending_searchall;
|
||||
case SEARCHTYPE_BUYING_STORE: return &buyingstore_searchall;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// checks if the player has a store by type
|
||||
static bool searchstore_hasstore(struct map_session_data* sd, unsigned char type)
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case SEARCHTYPE_VENDING: return (bool)( sd->vender_id != 0 );
|
||||
case SEARCHTYPE_BUYING_STORE: return sd->state.buyingstore;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// returns player's store id by type
|
||||
static int searchstore_getstoreid(struct map_session_data* sd, unsigned char type)
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case SEARCHTYPE_VENDING: return sd->vender_id;
|
||||
case SEARCHTYPE_BUYING_STORE: return sd->buyer_id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool searchstore_open(struct map_session_data* sd, unsigned int uses, unsigned short effect)
|
||||
{
|
||||
if( !battle_config.feature_search_stores || sd->searchstore.open )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !uses || effect >= EFFECTTYPE_MAX )
|
||||
{// invalid input
|
||||
return false;
|
||||
}
|
||||
|
||||
sd->searchstore.open = true;
|
||||
sd->searchstore.uses = uses;
|
||||
sd->searchstore.effect = effect;
|
||||
|
||||
clif_open_search_store_info(sd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned int min_price, unsigned int max_price, const unsigned short* itemlist, unsigned int item_count, const unsigned short* cardlist, unsigned int card_count)
|
||||
{
|
||||
unsigned int i;
|
||||
struct map_session_data* pl_sd;
|
||||
struct s_mapiterator* iter;
|
||||
struct s_search_store_search s;
|
||||
searchstore_searchall_t store_searchall;
|
||||
time_t querytime;
|
||||
|
||||
if( !battle_config.feature_search_stores )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !sd->searchstore.open )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( ( store_searchall = searchstore_getsearchallfunc(type) ) == NULL )
|
||||
{
|
||||
ShowError("searchstore_query: Unknown search type %u (account_id=%d).\n", (unsigned int)type, sd->bl.id);
|
||||
return;
|
||||
}
|
||||
|
||||
time(&querytime);
|
||||
|
||||
if( sd->searchstore.nextquerytime > querytime )
|
||||
{
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_LIMIT_SEARCH_TIME);
|
||||
return;
|
||||
}
|
||||
|
||||
if( !sd->searchstore.uses )
|
||||
{
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_SEARCH_CNT);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate lists
|
||||
for( i = 0; i < item_count; i++ )
|
||||
{
|
||||
if( !itemdb_exists(itemlist[i]) )
|
||||
{
|
||||
ShowWarning("searchstore_query: Client resolved item %hu is not known.\n", itemlist[i]);
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_NOTHING_SEARCH_ITEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for( i = 0; i < card_count; i++ )
|
||||
{
|
||||
if( !itemdb_exists(cardlist[i]) )
|
||||
{
|
||||
ShowWarning("searchstore_query: Client resolved card %hu is not known.\n", cardlist[i]);
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_NOTHING_SEARCH_ITEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( max_price < min_price )
|
||||
{
|
||||
swap(min_price, max_price);
|
||||
}
|
||||
|
||||
sd->searchstore.uses--;
|
||||
sd->searchstore.type = type;
|
||||
sd->searchstore.nextquerytime = querytime+battle_config.searchstore_querydelay;
|
||||
|
||||
// drop previous results
|
||||
searchstore_clear(sd);
|
||||
|
||||
// allocate max. amount of results
|
||||
sd->searchstore.items = aMalloc(sizeof(struct s_search_store_info_item)*battle_config.searchstore_maxresults);
|
||||
|
||||
// search
|
||||
s.search_sd = sd;
|
||||
s.itemlist = itemlist;
|
||||
s.cardlist = cardlist;
|
||||
s.item_count = item_count;
|
||||
s.card_count = card_count;
|
||||
s.min_price = min_price;
|
||||
s.max_price = max_price;
|
||||
iter = mapit_geteachpc();
|
||||
|
||||
for( pl_sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); pl_sd = (struct map_session_data*)mapit_next(iter) )
|
||||
{
|
||||
if( sd == pl_sd )
|
||||
{// skip own shop, if any
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !store_searchall(pl_sd, &s) )
|
||||
{// exceeded result size
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_OVER_MAXCOUNT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mapit_free(iter);
|
||||
|
||||
if( sd->searchstore.count )
|
||||
{
|
||||
// reclaim unused memory
|
||||
sd->searchstore.items = aRealloc(sd->searchstore.items, sizeof(struct s_search_store_info_item)*sd->searchstore.count);
|
||||
|
||||
// present results
|
||||
clif_search_store_info_ack(sd);
|
||||
|
||||
// one page displayed
|
||||
sd->searchstore.pages++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cleanup
|
||||
searchstore_clear(sd);
|
||||
|
||||
// update uses
|
||||
clif_search_store_info_ack(sd);
|
||||
|
||||
// notify of failure
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_NOTHING_SEARCH_ITEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// checks whether or not more results are available for the client
|
||||
bool searchstore_querynext(struct map_session_data* sd)
|
||||
{
|
||||
if( sd->searchstore.count && ( sd->searchstore.count-1 )/SEARCHSTORE_RESULTS_PER_PAGE < sd->searchstore.pages )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void searchstore_next(struct map_session_data* sd)
|
||||
{
|
||||
if( !battle_config.feature_search_stores || !sd->searchstore.open || sd->searchstore.count <= sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE )
|
||||
{// nothing (more) to display
|
||||
return;
|
||||
}
|
||||
|
||||
// present results
|
||||
clif_search_store_info_ack(sd);
|
||||
|
||||
// one more page displayed
|
||||
sd->searchstore.pages++;
|
||||
}
|
||||
|
||||
|
||||
void searchstore_clear(struct map_session_data* sd)
|
||||
{
|
||||
searchstore_clearremote(sd);
|
||||
|
||||
if( sd->searchstore.items )
|
||||
{// release results
|
||||
aFree(sd->searchstore.items);
|
||||
sd->searchstore.items = NULL;
|
||||
}
|
||||
|
||||
sd->searchstore.count = 0;
|
||||
sd->searchstore.pages = 0;
|
||||
}
|
||||
|
||||
|
||||
void searchstore_close(struct map_session_data* sd)
|
||||
{
|
||||
if( sd->searchstore.open )
|
||||
{
|
||||
searchstore_clear(sd);
|
||||
|
||||
sd->searchstore.uses = 0;
|
||||
sd->searchstore.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void searchstore_click(struct map_session_data* sd, int account_id, int store_id, unsigned short nameid)
|
||||
{
|
||||
unsigned int i;
|
||||
struct map_session_data* pl_sd;
|
||||
searchstore_search_t store_search;
|
||||
|
||||
if( !battle_config.feature_search_stores || !sd->searchstore.open || !sd->searchstore.count )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
searchstore_clearremote(sd);
|
||||
|
||||
ARR_FIND( 0, sd->searchstore.count, i, sd->searchstore.items[i].store_id == store_id && sd->searchstore.items[i].account_id == account_id && sd->searchstore.items[i].nameid == nameid );
|
||||
if( i == sd->searchstore.count )
|
||||
{// no such result, crafted
|
||||
ShowWarning("searchstore_click: Received request with item %hu of account %d, which is not part of current result set (account_id=%d, char_id=%d).\n", nameid, account_id, sd->bl.id, sd->status.char_id);
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE);
|
||||
return;
|
||||
}
|
||||
|
||||
if( ( pl_sd = map_id2sd(account_id) ) == NULL )
|
||||
{// no longer online
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE);
|
||||
return;
|
||||
}
|
||||
|
||||
if( !searchstore_hasstore(pl_sd, sd->searchstore.type) || searchstore_getstoreid(pl_sd, sd->searchstore.type) != store_id )
|
||||
{// no longer vending/buying or not same shop
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE);
|
||||
return;
|
||||
}
|
||||
|
||||
store_search = searchstore_getsearchfunc(sd->searchstore.type);
|
||||
|
||||
if( !store_search(pl_sd, nameid) )
|
||||
{// item no longer being sold/bought
|
||||
clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch( sd->searchstore.effect )
|
||||
{
|
||||
case EFFECTTYPE_NORMAL:
|
||||
// display coords
|
||||
|
||||
if( sd->bl.m != pl_sd->bl.m )
|
||||
{// not on same map, wipe previous marker
|
||||
clif_search_store_info_click_ack(sd, -1, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
clif_search_store_info_click_ack(sd, pl_sd->bl.x, pl_sd->bl.y);
|
||||
}
|
||||
|
||||
break;
|
||||
case EFFECTTYPE_CASH:
|
||||
// open remotely
|
||||
|
||||
// to bypass range checks
|
||||
sd->searchstore.remote_id = account_id;
|
||||
|
||||
switch( sd->searchstore.type )
|
||||
{
|
||||
case SEARCHTYPE_VENDING: vending_vendinglistreq(sd, account_id); break;
|
||||
case SEARCHTYPE_BUYING_STORE: buyingstore_open(sd, account_id); break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// unknown
|
||||
ShowError("searchstore_click: Unknown search store effect %u (account_id=%d).\n", (unsigned int)sd->searchstore.effect, sd->bl.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// checks whether or not sd has opened account_id's shop remotely
|
||||
bool searchstore_queryremote(struct map_session_data* sd, int account_id)
|
||||
{
|
||||
return (bool)( sd->searchstore.open && sd->searchstore.count && sd->searchstore.remote_id == account_id );
|
||||
}
|
||||
|
||||
|
||||
/// removes range-check bypassing for remotely opened stores
|
||||
void searchstore_clearremote(struct map_session_data* sd)
|
||||
{
|
||||
sd->searchstore.remote_id = 0;
|
||||
}
|
||||
|
||||
|
||||
/// receives results from a store-specific callback
|
||||
bool searchstore_result(struct map_session_data* sd, int store_id, int account_id, const char* store_name, unsigned short nameid, unsigned short amount, unsigned int price, const short* card, unsigned char refine)
|
||||
{
|
||||
struct s_search_store_info_item* ssitem;
|
||||
|
||||
if( sd->searchstore.count >= (unsigned int)battle_config.searchstore_maxresults )
|
||||
{// no more
|
||||
return false;
|
||||
}
|
||||
|
||||
ssitem = &sd->searchstore.items[sd->searchstore.count++];
|
||||
ssitem->store_id = store_id;
|
||||
ssitem->account_id = account_id;
|
||||
safestrncpy(ssitem->store_name, store_name, sizeof(ssitem->store_name));
|
||||
ssitem->nameid = nameid;
|
||||
ssitem->amount = amount;
|
||||
ssitem->price = price;
|
||||
memcpy(ssitem->card, card, sizeof(ssitem->card));
|
||||
ssitem->refine = refine;
|
||||
|
||||
return true;
|
||||
}
|
57
src/map/searchstore.h
Normal file
57
src/map/searchstore.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _SEARCHSTORE_H_
|
||||
#define _SEARCHSTORE_H_
|
||||
|
||||
#define SEARCHSTORE_RESULTS_PER_PAGE 10
|
||||
|
||||
/// information about the search being performed
|
||||
struct s_search_store_search
|
||||
{
|
||||
struct map_session_data* search_sd; // sd of the searching player
|
||||
const unsigned short* itemlist;
|
||||
const unsigned short* cardlist;
|
||||
unsigned int item_count;
|
||||
unsigned int card_count;
|
||||
unsigned int min_price;
|
||||
unsigned int max_price;
|
||||
};
|
||||
|
||||
struct s_search_store_info_item
|
||||
{
|
||||
int store_id;
|
||||
int account_id;
|
||||
char store_name[MESSAGE_SIZE];
|
||||
unsigned short nameid;
|
||||
unsigned short amount;
|
||||
unsigned int price;
|
||||
short card[MAX_SLOTS];
|
||||
unsigned char refine;
|
||||
};
|
||||
|
||||
struct s_search_store_info
|
||||
{
|
||||
unsigned int count;
|
||||
struct s_search_store_info_item* items;
|
||||
unsigned int pages; // amount of pages already sent to client
|
||||
unsigned int uses;
|
||||
int remote_id;
|
||||
time_t nextquerytime;
|
||||
unsigned short effect; // 0 = Normal (display coords), 1 = Cash (remote open store)
|
||||
unsigned char type; // 0 = Vending, 1 = Buying Store
|
||||
bool open;
|
||||
};
|
||||
|
||||
bool searchstore_open(struct map_session_data* sd, unsigned int uses, unsigned short effect);
|
||||
void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned int min_price, unsigned int max_price, const unsigned short* itemlist, unsigned int item_count, const unsigned short* cardlist, unsigned int card_count);
|
||||
bool searchstore_querynext(struct map_session_data* sd);
|
||||
void searchstore_next(struct map_session_data* sd);
|
||||
void searchstore_clear(struct map_session_data* sd);
|
||||
void searchstore_close(struct map_session_data* sd);
|
||||
void searchstore_click(struct map_session_data* sd, int account_id, int store_id, unsigned short nameid);
|
||||
bool searchstore_queryremote(struct map_session_data* sd, int account_id);
|
||||
void searchstore_clearremote(struct map_session_data* sd);
|
||||
bool searchstore_result(struct map_session_data* sd, int store_id, int account_id, const char* store_name, unsigned short nameid, unsigned short amount, unsigned int price, const short* card, unsigned char refine);
|
||||
|
||||
#endif // _SEARCHSTORE_H_
|
@ -1874,6 +1874,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
|
||||
if(sd->vender_id)
|
||||
vending_closevending(sd);
|
||||
buyingstore_close(sd);
|
||||
searchstore_close(sd);
|
||||
if(sd->state.storage_flag == 1)
|
||||
storage_storage_quit(sd,0);
|
||||
else if (sd->state.storage_flag == 2)
|
||||
|
@ -87,8 +87,11 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) )
|
||||
if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) )
|
||||
return; // shop too far away
|
||||
|
||||
searchstore_clearremote(sd);
|
||||
|
||||
if( count < 1 || count > MAX_VENDING || count > vsd->vend_num )
|
||||
return; // invalid amount of purchased items
|
||||
|
||||
@ -314,3 +317,88 @@ void vending_openvending(struct map_session_data* sd, const char* message, bool
|
||||
clif_openvending(sd,sd->bl.id,sd->vending);
|
||||
clif_showvendingboard(&sd->bl,message,0);
|
||||
}
|
||||
|
||||
|
||||
/// Checks if an item is being sold in given player's vending.
|
||||
bool vending_search(struct map_session_data* sd, unsigned short nameid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if( !sd->vender_id )
|
||||
{// not vending
|
||||
return false;
|
||||
}
|
||||
|
||||
ARR_FIND( 0, sd->vend_num, i, sd->status.cart[sd->vending[i].index].nameid == (short)nameid );
|
||||
if( i == sd->vend_num )
|
||||
{// not found
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Searches for all items in a vending, that match given ids, price and possible cards.
|
||||
/// @return Whether or not the search should be continued.
|
||||
bool vending_searchall(struct map_session_data* sd, const struct s_search_store_search* s)
|
||||
{
|
||||
int i, c, slot;
|
||||
unsigned int idx, cidx;
|
||||
struct item* it;
|
||||
|
||||
if( !sd->vender_id )
|
||||
{// not vending
|
||||
return true;
|
||||
}
|
||||
|
||||
for( idx = 0; idx < s->item_count; idx++ )
|
||||
{
|
||||
ARR_FIND( 0, sd->vend_num, i, sd->status.cart[sd->vending[i].index].nameid == (short)s->itemlist[idx] );
|
||||
if( i == sd->vend_num )
|
||||
{// not found
|
||||
continue;
|
||||
}
|
||||
it = &sd->status.cart[sd->vending[i].index];
|
||||
|
||||
if( s->min_price && s->min_price > sd->vending[i].value )
|
||||
{// too low price
|
||||
continue;
|
||||
}
|
||||
|
||||
if( s->max_price && s->max_price < sd->vending[i].value )
|
||||
{// too high price
|
||||
continue;
|
||||
}
|
||||
|
||||
if( s->card_count )
|
||||
{// check cards
|
||||
if( itemdb_isspecial(it->card[0]) )
|
||||
{// something, that is not a carded
|
||||
continue;
|
||||
}
|
||||
slot = itemdb_slot(it->nameid);
|
||||
|
||||
for( c = 0; c < slot && it->card[c]; c ++ )
|
||||
{
|
||||
ARR_FIND( 0, s->card_count, cidx, s->cardlist[cidx] == it->card[c] );
|
||||
if( cidx != s->card_count )
|
||||
{// found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( c == slot || !it->card[c] )
|
||||
{// no card match
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if( !searchstore_result(s->search_sd, sd->vender_id, sd->status.account_id, sd->message, it->nameid, sd->vending[i].amount, sd->vending[i].value, it->card, it->refine) )
|
||||
{// result set full
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "../common/cbasetypes.h"
|
||||
//#include "map.h"
|
||||
struct map_session_data;
|
||||
struct s_search_store_search;
|
||||
|
||||
struct s_vending {
|
||||
short index;
|
||||
@ -18,5 +19,7 @@ void vending_closevending(struct map_session_data* sd);
|
||||
void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count);
|
||||
void vending_vendinglistreq(struct map_session_data* sd, int id);
|
||||
void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count);
|
||||
bool vending_search(struct map_session_data* sd, unsigned short nameid);
|
||||
bool vending_searchall(struct map_session_data* sd, const struct s_search_store_search* s);
|
||||
|
||||
#endif /* _VENDING_H_ */
|
||||
|
@ -169,6 +169,7 @@
|
||||
<ClInclude Include="..\src\map\pet.h" />
|
||||
<ClInclude Include="..\src\map\quest.h" />
|
||||
<ClInclude Include="..\src\map\script.h" />
|
||||
<ClInclude Include="..\src\map\searchstore.h" />
|
||||
<ClInclude Include="..\src\map\skill.h" />
|
||||
<ClInclude Include="..\src\map\status.h" />
|
||||
<ClInclude Include="..\src\map\storage.h" />
|
||||
@ -220,6 +221,7 @@
|
||||
<ClCompile Include="..\src\map\pet.c" />
|
||||
<ClCompile Include="..\src\map\quest.c" />
|
||||
<ClCompile Include="..\src\map\script.c" />
|
||||
<ClCompile Include="..\src\map\searchstore.c" />
|
||||
<ClCompile Include="..\src\map\skill.c" />
|
||||
<ClCompile Include="..\src\map\status.c" />
|
||||
<ClCompile Include="..\src\map\storage.c" />
|
||||
|
@ -149,6 +149,7 @@
|
||||
<ClCompile Include="..\src\map\pet.c" />
|
||||
<ClCompile Include="..\src\map\quest.c" />
|
||||
<ClCompile Include="..\src\map\script.c" />
|
||||
<ClCompile Include="..\src\map\searchstore.c" />
|
||||
<ClCompile Include="..\src\map\skill.c" />
|
||||
<ClCompile Include="..\src\map\status.c" />
|
||||
<ClCompile Include="..\src\map\storage.c" />
|
||||
@ -198,6 +199,7 @@
|
||||
<ClInclude Include="..\src\map\pet.h" />
|
||||
<ClInclude Include="..\src\map\quest.h" />
|
||||
<ClInclude Include="..\src\map\script.h" />
|
||||
<ClInclude Include="..\src\map\searchstore.h" />
|
||||
<ClInclude Include="..\src\map\skill.h" />
|
||||
<ClInclude Include="..\src\map\status.h" />
|
||||
<ClInclude Include="..\src\map\storage.h" />
|
||||
|
@ -419,6 +419,14 @@ SOURCE=..\src\map\script.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\searchstore.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\searchstore.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\skill.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -303,6 +303,10 @@ SOURCE=..\src\map\script.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\searchstore.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\skill.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
@ -435,6 +439,10 @@ SOURCE=..\src\map\script.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\searchstore.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\src\map\skill.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -307,6 +307,12 @@
|
||||
<File
|
||||
RelativePath="..\src\map\script.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c">
|
||||
</File>
|
||||
|
@ -307,6 +307,12 @@
|
||||
<File
|
||||
RelativePath="..\src\map\script.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c">
|
||||
</File>
|
||||
|
@ -563,6 +563,14 @@
|
||||
RelativePath="..\src\map\script.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c"
|
||||
>
|
||||
|
@ -414,6 +414,14 @@
|
||||
RelativePath="..\src\map\script.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c"
|
||||
>
|
||||
|
@ -562,6 +562,14 @@
|
||||
RelativePath="..\src\map\script.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c"
|
||||
>
|
||||
|
@ -413,6 +413,14 @@
|
||||
RelativePath="..\src\map\script.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\searchstore.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\map\skill.c"
|
||||
>
|
||||
|
Loading…
x
Reference in New Issue
Block a user