Initial release of the cash shop sales (#1825)
Added a permission for the cashshop sales Thanks to @Angelic234 and everyone else who tested this feature while it was sleeping waiting in a pull request. Thanks to @aleos89, @secretdataz and @lighta for reviewing and commenting.
This commit is contained in:
parent
7cf081c24b
commit
aaa4ea919e
@ -293,6 +293,7 @@ groups: (
|
|||||||
item_unconditional: false
|
item_unconditional: false
|
||||||
bypass_stat_onclone: true
|
bypass_stat_onclone: true
|
||||||
bypass_max_stat: true
|
bypass_max_stat: true
|
||||||
|
cashshop_sale: true
|
||||||
/* all_permission: true */
|
/* all_permission: true */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ renewal-mob_skill_table: mob_skill_db_re
|
|||||||
mob_skill2_table: mob_skill_db2
|
mob_skill2_table: mob_skill_db2
|
||||||
renewal-mob_skill2_table: mob_skill_db2_re
|
renewal-mob_skill2_table: mob_skill_db2_re
|
||||||
mapreg_table: mapreg
|
mapreg_table: mapreg
|
||||||
|
sales_table: sales
|
||||||
vending_table: vendings
|
vending_table: vendings
|
||||||
vending_items_table: vending_items
|
vending_items_table: vending_items
|
||||||
market_table: market
|
market_table: market
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// 5: Buff
|
// 5: Buff
|
||||||
// 6: Heal
|
// 6: Heal
|
||||||
// 7: Other
|
// 7: Other
|
||||||
|
// 8: Sale
|
||||||
//
|
//
|
||||||
// Price:
|
// Price:
|
||||||
// Item cost, in cash points (#CASHPOINTS).
|
// Item cost, in cash points (#CASHPOINTS).
|
||||||
|
@ -2322,7 +2322,6 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto]
|
|||||||
0x08A4,36,storagepassword,2:4:20
|
0x08A4,36,storagepassword,2:4:20
|
||||||
//New Packets
|
//New Packets
|
||||||
//0x097E,12 //ZC_UPDATE_RANKING_POINT
|
//0x097E,12 //ZC_UPDATE_RANKING_POINT
|
||||||
0x09B4,6,dull,0 //Cash Shop - Special Tab
|
|
||||||
0x09CE,102,itemmonster,2
|
0x09CE,102,itemmonster,2
|
||||||
0x09D4,2,npcshopclosed,0
|
0x09D4,2,npcshopclosed,0
|
||||||
//NPC Market
|
//NPC Market
|
||||||
@ -2336,6 +2335,19 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto]
|
|||||||
0x098A,-1
|
0x098A,-1
|
||||||
0x098D,-1,clanchat,2:4
|
0x098D,-1,clanchat,2:4
|
||||||
0x098E,-1
|
0x098E,-1
|
||||||
|
// Sale
|
||||||
|
0x09AC,-1,salesearch,2:4:8
|
||||||
|
0x09AD,8
|
||||||
|
0x09AE,17,saleadd,2:6:8:12:16
|
||||||
|
0x09AF,4
|
||||||
|
0x09B0,8,saleremove,2:6
|
||||||
|
0x09B1,4
|
||||||
|
0x09B2,8
|
||||||
|
0x09B3,4
|
||||||
|
0x09B4,6,saleopen,2
|
||||||
|
0x09BC,6,saleclose,2
|
||||||
|
0x09C3,8,salerefresh,2:6
|
||||||
|
0x09C4,8
|
||||||
|
|
||||||
// New Packet
|
// New Packet
|
||||||
0x097A,-1 // ZC_ALL_QUEST_LIST2
|
0x097A,-1 // ZC_ALL_QUEST_LIST2
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// 5: Buff
|
// 5: Buff
|
||||||
// 6: Heal
|
// 6: Heal
|
||||||
// 7: Other
|
// 7: Other
|
||||||
|
// 8: Sale
|
||||||
//
|
//
|
||||||
// Price:
|
// Price:
|
||||||
// Item cost, in cash points (#CASHPOINTS).
|
// Item cost, in cash points (#CASHPOINTS).
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// 5: Buff
|
// 5: Buff
|
||||||
// 6: Heal
|
// 6: Heal
|
||||||
// 7: Other
|
// 7: Other
|
||||||
|
// 8: Sale
|
||||||
//
|
//
|
||||||
// Price:
|
// Price:
|
||||||
// Item cost, in cash points (#CASHPOINTS).
|
// Item cost, in cash points (#CASHPOINTS).
|
||||||
|
@ -813,6 +813,18 @@ CREATE TABLE IF NOT EXISTS `mercenary_owner` (
|
|||||||
PRIMARY KEY (`char_id`)
|
PRIMARY KEY (`char_id`)
|
||||||
) ENGINE=MyISAM;
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for `sales`
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `sales` (
|
||||||
|
`nameid` smallint(5) unsigned NOT NULL,
|
||||||
|
`start` datetime NOT NULL,
|
||||||
|
`end` datetime NOT NULL,
|
||||||
|
`amount` int(11) NOT NULL,
|
||||||
|
PRIMARY KEY (`nameid`)
|
||||||
|
) ENGINE=MyISAM;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `sc_data`
|
-- Table structure for table `sc_data`
|
||||||
--
|
--
|
||||||
|
11
sql-files/upgrades/upgrade_20170215.sql
Normal file
11
sql-files/upgrades/upgrade_20170215.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for `sales`
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `sales` (
|
||||||
|
`nameid` smallint(5) unsigned NOT NULL,
|
||||||
|
`start` datetime NOT NULL,
|
||||||
|
`end` datetime NOT NULL,
|
||||||
|
`amount` int(11) NOT NULL,
|
||||||
|
PRIMARY KEY (`nameid`)
|
||||||
|
) ENGINE=MyISAM;
|
@ -31,6 +31,9 @@
|
|||||||
/// Check if the client needs delete_date as remaining time and not the actual delete_date (actually it was tested for clients since 2013)
|
/// Check if the client needs delete_date as remaining time and not the actual delete_date (actually it was tested for clients since 2013)
|
||||||
#define PACKETVER_CHAR_DELETEDATE (PACKETVER > 20130000 && PACKETVER < 20141016) || PACKETVER >= 20150826
|
#define PACKETVER_CHAR_DELETEDATE (PACKETVER > 20130000 && PACKETVER < 20141016) || PACKETVER >= 20150826
|
||||||
|
|
||||||
|
// Check if the specified packetvresion supports the cashshop sale system
|
||||||
|
#define PACKETVER_SUPPORTS_SALES PACKETVER>=20131223
|
||||||
|
|
||||||
///Remove/Comment this line to disable sc_data saving. [Skotlex]
|
///Remove/Comment this line to disable sc_data saving. [Skotlex]
|
||||||
#define ENABLE_SC_SAVING
|
#define ENABLE_SC_SAVING
|
||||||
/** Remove/Comment this line to disable server-side hot-key saving support [Skotlex]
|
/** Remove/Comment this line to disable server-side hot-key saving support [Skotlex]
|
||||||
|
@ -11,11 +11,15 @@
|
|||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include <stdlib.h> // atoi
|
#include <stdlib.h> // atoi
|
||||||
|
|
||||||
struct cash_item_db cash_shop_items[CASHSHOP_TAB_SEARCH];
|
struct cash_item_db cash_shop_items[CASHSHOP_TAB_MAX];
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
struct sale_item_db sale_items;
|
||||||
|
#endif
|
||||||
bool cash_shop_defined = false;
|
bool cash_shop_defined = false;
|
||||||
|
|
||||||
extern char item_cash_table[32];
|
extern char item_cash_table[32];
|
||||||
extern char item_cash2_table[32];
|
extern char item_cash2_table[32];
|
||||||
|
extern char sales_table[32];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads one line from database and assigns it to RAM.
|
* Reads one line from database and assigns it to RAM.
|
||||||
@ -35,7 +39,7 @@ static bool cashshop_parse_dbrow(char* fields[], int columns, int current) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( tab > CASHSHOP_TAB_SEARCH ){
|
if( tab >= CASHSHOP_TAB_MAX ){
|
||||||
ShowWarning( "cashshop_parse_dbrow: Invalid tab %d in line '%d', skipping...\n", tab, current );
|
ShowWarning( "cashshop_parse_dbrow: Invalid tab %d in line '%d', skipping...\n", tab, current );
|
||||||
return 0;
|
return 0;
|
||||||
}else if( price < 1 ){
|
}else if( price < 1 ){
|
||||||
@ -139,16 +143,314 @@ static int cashshop_read_db_sql( void ){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
static bool sale_parse_dbrow( char* fields[], int columns, int current ){
|
||||||
|
unsigned short nameid = atoi(fields[0]);
|
||||||
|
int start = atoi(fields[1]), end = atoi(fields[2]), amount = atoi(fields[3]), i;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct sale_item_data* sale_item = NULL;
|
||||||
|
|
||||||
|
if( !itemdb_exists(nameid) ){
|
||||||
|
ShowWarning( "sale_parse_dbrow: Invalid ID %hu in line '%d', skipping...\n", nameid, current );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARR_FIND( 0, cash_shop_items[CASHSHOP_TAB_SALE].count, i, cash_shop_items[CASHSHOP_TAB_SALE].item[i]->nameid == nameid );
|
||||||
|
|
||||||
|
if( i == cash_shop_items[CASHSHOP_TAB_SALE].count ){
|
||||||
|
ShowWarning( "sale_parse_dbrow: ID %hu is not registered in the limited tab in line '%d', skipping...\n", nameid, current );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the end is after the start
|
||||||
|
if( start >= end ){
|
||||||
|
ShowWarning( "sale_parse_dbrow: Sale for item %hu was ignored, because the timespan was not correct.\n", nameid );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it is already in the past
|
||||||
|
if( end < now ){
|
||||||
|
ShowWarning( "sale_parse_dbrow: An outdated sale for item %hu was ignored.\n", nameid );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there is already an entry
|
||||||
|
sale_item = sale_find_item(nameid,false);
|
||||||
|
|
||||||
|
if( sale_item == NULL ){
|
||||||
|
RECREATE(sale_items.item, struct sale_item_data *, ++sale_items.count);
|
||||||
|
CREATE(sale_items.item[sale_items.count - 1], struct sale_item_data, 1);
|
||||||
|
sale_item = sale_items.item[sale_items.count - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
sale_item->nameid = nameid;
|
||||||
|
sale_item->start = start;
|
||||||
|
sale_item->end = end;
|
||||||
|
sale_item->amount = amount;
|
||||||
|
sale_item->timer_start = INVALID_TIMER;
|
||||||
|
sale_item->timer_end = INVALID_TIMER;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sale_read_db_sql( void ){
|
||||||
|
uint32 lines = 0, count = 0;
|
||||||
|
|
||||||
|
if( SQL_ERROR == Sql_Query( mmysql_handle, "SELECT `nameid`, UNIX_TIMESTAMP(`start`), UNIX_TIMESTAMP(`end`), `amount` FROM `%s` WHERE `end` > now()", sales_table ) ){
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ){
|
||||||
|
char* str[4];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lines++;
|
||||||
|
|
||||||
|
for( i = 0; i < 4; i++ ){
|
||||||
|
Sql_GetData( mmysql_handle, i, &str[i], NULL );
|
||||||
|
|
||||||
|
if( str[i] == NULL ){
|
||||||
|
str[i] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !sale_parse_dbrow( str, 4, lines ) ){
|
||||||
|
ShowError( "sale_read_db_sql: Cannot process table '%s' at line '%d', skipping...\n", sales_table, lines );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sql_FreeResult(mmysql_handle);
|
||||||
|
|
||||||
|
ShowStatus( "Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, sales_table );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sale_end_timer( int tid, unsigned int tick, int id, intptr_t data ){
|
||||||
|
struct sale_item_data* sale_item = (struct sale_item_data*)data;
|
||||||
|
|
||||||
|
// Remove the timer so the sale end is not sent out again
|
||||||
|
delete_timer( sale_item->timer_end, sale_end_timer );
|
||||||
|
sale_item->timer_end = INVALID_TIMER;
|
||||||
|
|
||||||
|
clif_sale_end( sale_item, NULL, ALL_CLIENT );
|
||||||
|
|
||||||
|
sale_remove_item( sale_item->nameid );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sale_start_timer( int tid, unsigned int tick, int id, intptr_t data ){
|
||||||
|
struct sale_item_data* sale_item = (struct sale_item_data*)data;
|
||||||
|
|
||||||
|
clif_sale_start( sale_item, NULL, ALL_CLIENT );
|
||||||
|
clif_sale_amount( sale_item, NULL, ALL_CLIENT );
|
||||||
|
|
||||||
|
// Clear the start timer
|
||||||
|
if( sale_item->timer_start != INVALID_TIMER ){
|
||||||
|
delete_timer( sale_item->timer_start, sale_start_timer );
|
||||||
|
sale_item->timer_start = INVALID_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sale end
|
||||||
|
sale_item->timer_end = add_timer( gettick() + (unsigned int)( sale_item->end - time(NULL) ) * 1000, sale_end_timer, 0, (intptr_t)sale_item );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum e_sale_add_result sale_add_item( uint16 nameid, int32 count, time_t from, time_t to ){
|
||||||
|
int i;
|
||||||
|
struct sale_item_data* sale_item;
|
||||||
|
|
||||||
|
// Check if the item exists in the sales tab
|
||||||
|
ARR_FIND( 0, cash_shop_items[CASHSHOP_TAB_SALE].count, i, cash_shop_items[CASHSHOP_TAB_SALE].item[i]->nameid == nameid );
|
||||||
|
|
||||||
|
// Item does not exist in the sales tab
|
||||||
|
if( i == cash_shop_items[CASHSHOP_TAB_SALE].count ){
|
||||||
|
return SALE_ADD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a sale in the past is not possible
|
||||||
|
if( from < time(NULL) ){
|
||||||
|
return SALE_ADD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The end has to be after the start
|
||||||
|
if( from >= to ){
|
||||||
|
return SALE_ADD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Amount has to be positive - this should be limited from the client too
|
||||||
|
if( count == 0 ){
|
||||||
|
return SALE_ADD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a sale of this item already exists
|
||||||
|
if( sale_find_item(nameid, false) ){
|
||||||
|
return SALE_ADD_DUPLICATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`nameid`,`start`,`end`,`amount`) VALUES ( '%d', FROM_UNIXTIME(%d), FROM_UNIXTIME(%d), '%d' )", sales_table, nameid, (uint32)from, (uint32)to, count) ){
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
return SALE_ADD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECREATE(sale_items.item, struct sale_item_data *, ++sale_items.count);
|
||||||
|
CREATE(sale_items.item[sale_items.count - 1], struct sale_item_data, 1);
|
||||||
|
sale_item = sale_items.item[sale_items.count - 1];
|
||||||
|
|
||||||
|
sale_item->nameid = nameid;
|
||||||
|
sale_item->start = from;
|
||||||
|
sale_item->end = to;
|
||||||
|
sale_item->amount = count;
|
||||||
|
sale_item->timer_start = add_timer( gettick() + (unsigned int)(from - time(NULL)) * 1000, sale_start_timer, 0, (intptr_t)sale_item );
|
||||||
|
sale_item->timer_end = INVALID_TIMER;
|
||||||
|
|
||||||
|
return SALE_ADD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sale_remove_item( uint16 nameid ){
|
||||||
|
struct sale_item_data* sale_item;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Check if there is an entry for this item id
|
||||||
|
if( !sale_find_item(nameid, false) ){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete it from the database
|
||||||
|
if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `nameid` = '%d'", sales_table, nameid ) ){
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the sale is currently running
|
||||||
|
sale_item = sale_find_item(nameid, true);
|
||||||
|
|
||||||
|
if( sale_item != NULL && sale_item->timer_end != INVALID_TIMER ){
|
||||||
|
// Notify all clients that the sale has ended
|
||||||
|
clif_sale_end(sale_item, NULL, ALL_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sale_item->timer_start != INVALID_TIMER ){
|
||||||
|
delete_timer(sale_item->timer_start, sale_start_timer);
|
||||||
|
sale_item->timer_start = INVALID_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sale_item->timer_end != INVALID_TIMER ){
|
||||||
|
delete_timer(sale_item->timer_end, sale_end_timer);
|
||||||
|
sale_item->timer_end = INVALID_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the original pointer in the array
|
||||||
|
ARR_FIND( 0, sale_items.count, i, sale_items.item[i] == sale_item );
|
||||||
|
|
||||||
|
// Is there still any entry left?
|
||||||
|
if( --sale_items.count > 0 ){
|
||||||
|
// fill the hole by moving the rest
|
||||||
|
for( ; i < sale_items.count; i++ ){
|
||||||
|
memcpy( sale_items.item[i], sale_items.item[i + 1], sizeof(struct sale_item_data) );
|
||||||
|
}
|
||||||
|
|
||||||
|
aFree(sale_items.item[i]);
|
||||||
|
|
||||||
|
RECREATE(sale_items.item, struct sale_item_data *, sale_items.count);
|
||||||
|
}else{
|
||||||
|
aFree(sale_items.item[0]);
|
||||||
|
aFree(sale_items.item);
|
||||||
|
sale_items.item = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sale_item_data* sale_find_item( uint16 nameid, bool onsale ){
|
||||||
|
int i;
|
||||||
|
struct sale_item_data* sale_item;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
ARR_FIND( 0, sale_items.count, i, sale_items.item[i]->nameid == nameid );
|
||||||
|
|
||||||
|
// No item with the specified item id was found
|
||||||
|
if( i == sale_items.count ){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sale_item = sale_items.item[i];
|
||||||
|
|
||||||
|
// No need to check any further
|
||||||
|
if( !onsale ){
|
||||||
|
return sale_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sale is in the future
|
||||||
|
if( sale_items.item[i]->start > now ){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sale was in the past
|
||||||
|
if( sale_items.item[i]->end < now ){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The amount has been used up already
|
||||||
|
if( sale_items.item[i]->amount == 0 ){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the sale item
|
||||||
|
return sale_items.item[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void sale_notify_login( struct map_session_data* sd ){
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < sale_items.count; i++ ){
|
||||||
|
if( sale_items.item[i]->timer_end != INVALID_TIMER ){
|
||||||
|
clif_sale_start( sale_items.item[i], &sd->bl, SELF );
|
||||||
|
clif_sale_amount( sale_items.item[i], &sd->bl, SELF );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determines whether to read TXT or SQL database
|
* Determines whether to read TXT or SQL database
|
||||||
* based on 'db_use_sqldbs' in conf/map_athena.conf.
|
* based on 'db_use_sqldbs' in conf/map_athena.conf.
|
||||||
*/
|
*/
|
||||||
static void cashshop_read_db( void ){
|
static void cashshop_read_db( void ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int i;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
if( db_use_sqldbs ){
|
if( db_use_sqldbs ){
|
||||||
cashshop_read_db_sql();
|
cashshop_read_db_sql();
|
||||||
} else {
|
} else {
|
||||||
cashshop_read_db_txt();
|
cashshop_read_db_txt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
sale_read_db_sql();
|
||||||
|
|
||||||
|
// Clean outdated sales
|
||||||
|
if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `end` < FROM_UNIXTIME(%d)", sales_table, (uint32)now ) ){
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init next sale start, if there is any
|
||||||
|
for( i = 0; i < sale_items.count; i++ ){
|
||||||
|
struct sale_item_data* it = sale_items.item[i];
|
||||||
|
|
||||||
|
if( it->start > now ){
|
||||||
|
it->timer_start = add_timer( gettick() + (unsigned int)( it->start - time(NULL) ) * 1000, sale_start_timer, 0, (intptr_t)it );
|
||||||
|
}else{
|
||||||
|
sale_start_timer( 0, gettick(), 0, (intptr_t)it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attempts to purchase a cashshop item from the list.
|
/** Attempts to purchase a cashshop item from the list.
|
||||||
@ -164,6 +466,9 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
uint32 totalcash = 0;
|
uint32 totalcash = 0;
|
||||||
uint32 totalweight = 0;
|
uint32 totalweight = 0;
|
||||||
int i,new_;
|
int i,new_;
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
struct sale_item_data* sale;
|
||||||
|
#endif
|
||||||
|
|
||||||
if( sd == NULL || item_list == NULL || !cash_shop_defined){
|
if( sd == NULL || item_list == NULL || !cash_shop_defined){
|
||||||
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
||||||
@ -178,10 +483,10 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
for( i = 0; i < n; ++i ){
|
for( i = 0; i < n; ++i ){
|
||||||
unsigned short nameid = *( item_list + i * 5 );
|
unsigned short nameid = *( item_list + i * 5 );
|
||||||
uint32 quantity = *( item_list + i * 5 + 2 );
|
uint32 quantity = *( item_list + i * 5 + 2 );
|
||||||
uint16 tab = *( item_list + i * 5 + 4 );
|
uint8 tab = (uint8)*( item_list + i * 5 + 4 );
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if( tab > CASHSHOP_TAB_SEARCH ){
|
if( tab >= CASHSHOP_TAB_MAX ){
|
||||||
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -203,6 +508,32 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
quantity = *( item_list + i * 5 + 2 ) = 1;
|
quantity = *( item_list + i * 5 + 2 ) = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( quantity > 99 ){
|
||||||
|
// Client blocks buying more than 99 items of the same type at the same time, this means someone forged a packet with a higher quantity
|
||||||
|
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
if( tab == CASHSHOP_TAB_SALE ){
|
||||||
|
sale = sale_find_item( nameid, true );
|
||||||
|
|
||||||
|
if( sale == NULL ){
|
||||||
|
// Client tried to buy an item from sale that was not even on sale
|
||||||
|
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sale->amount < quantity ){
|
||||||
|
// Client tried to buy a higher quantity than is available
|
||||||
|
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
|
||||||
|
// Maybe he did not get refreshed in time -> do it now
|
||||||
|
clif_sale_amount( sale, &sd->bl, SELF );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch( pc_checkadditem( sd, nameid, quantity ) ){
|
switch( pc_checkadditem( sd, nameid, quantity ) ){
|
||||||
case CHKADDITEM_EXIST:
|
case CHKADDITEM_EXIST:
|
||||||
break;
|
break;
|
||||||
@ -236,6 +567,7 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
for( i = 0; i < n; ++i ){
|
for( i = 0; i < n; ++i ){
|
||||||
unsigned short nameid = *( item_list + i * 5 );
|
unsigned short nameid = *( item_list + i * 5 );
|
||||||
uint32 quantity = *( item_list + i * 5 + 2 );
|
uint32 quantity = *( item_list + i * 5 + 2 );
|
||||||
|
uint16 tab = *(item_list + i * 5 + 4);
|
||||||
struct item_data *id = itemdb_search(nameid);
|
struct item_data *id = itemdb_search(nameid);
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
@ -270,6 +602,24 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_RUNE_OVERCOUNT );
|
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_RUNE_OVERCOUNT );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
if( tab == CASHSHOP_TAB_SALE ){
|
||||||
|
uint32 new_amount = sale->amount - get_amt;
|
||||||
|
|
||||||
|
if( new_amount == 0 ){
|
||||||
|
sale_remove_item(sale->nameid);
|
||||||
|
}else{
|
||||||
|
if( SQL_ERROR == Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = '%d' WHERE `nameid` = '%d'", sales_table, new_amount, nameid ) ){
|
||||||
|
Sql_ShowDebug(mmysql_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
sale->amount = new_amount;
|
||||||
|
|
||||||
|
clif_sale_amount(sale, NULL, ALL_CLIENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,13 +643,38 @@ void cashshop_reloaddb( void ){
|
|||||||
void do_final_cashshop( void ){
|
void do_final_cashshop( void ){
|
||||||
int tab, i;
|
int tab, i;
|
||||||
|
|
||||||
for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_SEARCH; tab++ ){
|
for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_MAX; tab++ ){
|
||||||
for( i = 0; i < cash_shop_items[tab].count; i++ ){
|
for( i = 0; i < cash_shop_items[tab].count; i++ ){
|
||||||
aFree( cash_shop_items[tab].item[i] );
|
aFree( cash_shop_items[tab].item[i] );
|
||||||
}
|
}
|
||||||
aFree( cash_shop_items[tab].item );
|
aFree( cash_shop_items[tab].item );
|
||||||
}
|
}
|
||||||
memset( cash_shop_items, 0, sizeof( cash_shop_items ) );
|
memset( cash_shop_items, 0, sizeof( cash_shop_items ) );
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
if( sale_items.count > 0 ){
|
||||||
|
for( i = 0; i < sale_items.count; i++ ){
|
||||||
|
struct sale_item_data* it = sale_items.item[i];
|
||||||
|
|
||||||
|
if( it->timer_start != INVALID_TIMER ){
|
||||||
|
delete_timer( it->timer_start, sale_start_timer );
|
||||||
|
it->timer_start = INVALID_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( it->timer_end != INVALID_TIMER ){
|
||||||
|
delete_timer( it->timer_end, sale_end_timer );
|
||||||
|
it->timer_end = INVALID_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
aFree(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
aFree(sale_items.item);
|
||||||
|
|
||||||
|
sale_items.item = NULL;
|
||||||
|
sale_items.count = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -16,14 +16,17 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u
|
|||||||
enum CASH_SHOP_TAB_CODE
|
enum CASH_SHOP_TAB_CODE
|
||||||
{
|
{
|
||||||
CASHSHOP_TAB_NEW = 0x0,
|
CASHSHOP_TAB_NEW = 0x0,
|
||||||
CASHSHOP_TAB_POPULAR = 0x1,
|
CASHSHOP_TAB_POPULAR,
|
||||||
CASHSHOP_TAB_LIMITED = 0x2,
|
CASHSHOP_TAB_LIMITED,
|
||||||
CASHSHOP_TAB_RENTAL = 0x3,
|
CASHSHOP_TAB_RENTAL,
|
||||||
CASHSHOP_TAB_PERPETUITY = 0x4,
|
CASHSHOP_TAB_PERPETUITY,
|
||||||
CASHSHOP_TAB_BUFF = 0x5,
|
CASHSHOP_TAB_BUFF,
|
||||||
CASHSHOP_TAB_RECOVERY = 0x6,
|
CASHSHOP_TAB_RECOVERY,
|
||||||
CASHSHOP_TAB_ETC = 0x7,
|
CASHSHOP_TAB_ETC,
|
||||||
CASHSHOP_TAB_SEARCH = 0x8
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
CASHSHOP_TAB_SALE,
|
||||||
|
#endif
|
||||||
|
CASHSHOP_TAB_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
// PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT
|
// PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT
|
||||||
@ -54,7 +57,39 @@ struct cash_item_db{
|
|||||||
uint32 count;
|
uint32 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct cash_item_db cash_shop_items[CASHSHOP_TAB_SEARCH];
|
extern struct cash_item_db cash_shop_items[CASHSHOP_TAB_MAX];
|
||||||
extern bool cash_shop_defined;
|
extern bool cash_shop_defined;
|
||||||
|
|
||||||
|
enum e_sale_add_result {
|
||||||
|
SALE_ADD_SUCCESS = 0,
|
||||||
|
SALE_ADD_FAILED = 1,
|
||||||
|
SALE_ADD_DUPLICATE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sale_item_data{
|
||||||
|
// Data
|
||||||
|
uint16 nameid;
|
||||||
|
time_t start;
|
||||||
|
time_t end;
|
||||||
|
uint32 amount;
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
int timer_start;
|
||||||
|
int timer_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sale_item_db{
|
||||||
|
struct sale_item_data** item;
|
||||||
|
uint32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
extern struct sale_item_db sale_items;
|
||||||
|
|
||||||
|
struct sale_item_data* sale_find_item(uint16 nameid, bool onsale);
|
||||||
|
enum e_sale_add_result sale_add_item(uint16 nameid, int32 count, time_t from, time_t to);
|
||||||
|
bool sale_remove_item(uint16 nameid);
|
||||||
|
void sale_notify_login( struct map_session_data* sd );
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _CASHSHOP_H_ */
|
#endif /* _CASHSHOP_H_ */
|
||||||
|
275
src/map/clif.c
275
src/map/clif.c
@ -15403,7 +15403,7 @@ void clif_parse_CashShopReqTab(int fd, struct map_session_data *sd) {
|
|||||||
short tab = RFIFOW(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
short tab = RFIFOW(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if( tab < 0 || tab > CASHSHOP_TAB_SEARCH )
|
if( tab < 0 || tab >= CASHSHOP_TAB_MAX )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WFIFOHEAD(fd, 10 + ( cash_shop_items[tab].count * 6 ) );
|
WFIFOHEAD(fd, 10 + ( cash_shop_items[tab].count * 6 ) );
|
||||||
@ -15425,7 +15425,7 @@ void clif_parse_CashShopReqTab(int fd, struct map_session_data *sd) {
|
|||||||
void clif_cashshop_list( int fd ){
|
void clif_cashshop_list( int fd ){
|
||||||
int tab;
|
int tab;
|
||||||
|
|
||||||
for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_SEARCH; tab++ ){
|
for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_MAX; tab++ ){
|
||||||
int length = 8 + cash_shop_items[tab].count * 6;
|
int length = 8 + cash_shop_items[tab].count * 6;
|
||||||
int i, offset;
|
int i, offset;
|
||||||
|
|
||||||
@ -15448,6 +15448,9 @@ void clif_cashshop_list( int fd ){
|
|||||||
void clif_parse_cashshop_list_request( int fd, struct map_session_data* sd ){
|
void clif_parse_cashshop_list_request( int fd, struct map_session_data* sd ){
|
||||||
if( !sd->status.cashshop_sent ) {
|
if( !sd->status.cashshop_sent ) {
|
||||||
clif_cashshop_list( fd );
|
clif_cashshop_list( fd );
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
sale_notify_login(sd);
|
||||||
|
#endif
|
||||||
sd->status.cashshop_sent = true;
|
sd->status.cashshop_sent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18872,6 +18875,261 @@ void clif_hat_effect_single( struct map_session_data* sd, uint16 effectId, bool
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Notify the client that a sale has started
|
||||||
|
/// 09b2 <item id>.W <remaining time>.L (ZC_NOTIFY_BARGAIN_SALE_SELLING)
|
||||||
|
void clif_sale_start( struct sale_item_data* sale_item, struct block_list* bl, enum send_target target ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
unsigned char buf[8];
|
||||||
|
|
||||||
|
WBUFW(buf, 0) = 0x9b2;
|
||||||
|
WBUFW(buf, 2) = sale_item->nameid;
|
||||||
|
WBUFL(buf, 4) = (uint32)(sale_item->end - time(NULL)); // time in S
|
||||||
|
|
||||||
|
clif_send(buf, 8, bl, target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the clien that a sale has ended
|
||||||
|
/// 09b3 <item id>.W (ZC_NOTIFY_BARGAIN_SALE_CLOSE)
|
||||||
|
void clif_sale_end( struct sale_item_data* sale_item, struct block_list* bl, enum send_target target ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
unsigned char buf[4];
|
||||||
|
|
||||||
|
WBUFW(buf, 0) = 0x9b3;
|
||||||
|
WBUFW(buf, 2) = sale_item->nameid;
|
||||||
|
|
||||||
|
clif_send(buf, 4, bl, target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the remaining amount of a sale item.
|
||||||
|
/// 09c4 <item id>.W <amount>.L (ZC_ACK_COUNT_BARGAIN_SALE_ITEM)
|
||||||
|
void clif_sale_amount( struct sale_item_data* sale_item, struct block_list* bl, enum send_target target ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
unsigned char buf[8];
|
||||||
|
|
||||||
|
WBUFW(buf, 0) = 0x9c4;
|
||||||
|
WBUFW(buf, 2) = sale_item->nameid;
|
||||||
|
WBUFL(buf, 4) = sale_item->amount;
|
||||||
|
|
||||||
|
clif_send(buf, 8, bl, target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The client requested a refresh of the current remaining count of a sale item
|
||||||
|
/// 09ac <account id>.L <item id>.W (CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO)
|
||||||
|
void clif_parse_sale_refresh( int fd, struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
struct sale_item_data* sale;
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 2) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sale = sale_find_item( RFIFOW(fd, 6), true );
|
||||||
|
|
||||||
|
if( sale == NULL ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clif_sale_amount(sale, &sd->bl, SELF);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens the sale administration window on the client
|
||||||
|
/// 09b5 (ZC_OPEN_BARGAIN_SALE_TOOL)
|
||||||
|
void clif_sale_open( struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int fd = sd->fd;
|
||||||
|
|
||||||
|
// TODO: do we want state tracking?
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 2);
|
||||||
|
WFIFOW(fd, 0) = 0x9b5;
|
||||||
|
WFIFOSET(fd, 2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Client request to open the sale administration window.
|
||||||
|
/// This is sent by /limitedsale
|
||||||
|
/// 09b4 <account id>.L (CZ_OPEN_BARGAIN_SALE_TOOL)
|
||||||
|
void clif_parse_sale_open( int fd, struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 2) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pc_has_permission( sd, PC_PERM_CASHSHOP_SALE ) ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clif_sale_open(sd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the sale administration window on the client.
|
||||||
|
/// 09bd (ZC_CLOSE_BARGAIN_SALE_TOOL)
|
||||||
|
void clif_sale_close(struct map_session_data* sd) {
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int fd = sd->fd;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 2);
|
||||||
|
WFIFOW(fd, 0) = 0x9bd;
|
||||||
|
WFIFOSET(fd, 2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Client request to close the sale administration window.
|
||||||
|
/// 09bc (CZ_CLOSE_BARGAIN_SALE_TOOL)
|
||||||
|
void clif_parse_sale_close(int fd, struct map_session_data* sd) {
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 2) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do we want state tracking?
|
||||||
|
|
||||||
|
clif_sale_close(sd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reply to a item search request for item sale administration.
|
||||||
|
/// 09ad <result>.W <item id>.W <price>.L (ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO)
|
||||||
|
void clif_sale_search_reply( struct map_session_data* sd, struct cash_item_data* item ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int fd = sd->fd;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 10);
|
||||||
|
WFIFOW(fd, 0) = 0x9ad;
|
||||||
|
if( item != NULL ){
|
||||||
|
WFIFOW(fd, 2) = 0;
|
||||||
|
WFIFOW(fd, 4) = item->nameid;
|
||||||
|
WFIFOL(fd, 6) = item->price;
|
||||||
|
}else{
|
||||||
|
WFIFOW(fd, 2) = 1;
|
||||||
|
WFIFOW(fd, 4) = 0;
|
||||||
|
WFIFOL(fd, 6) = 0;
|
||||||
|
}
|
||||||
|
WFIFOSET(fd, 10);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search request for an item sale administration.
|
||||||
|
/// 09ac <length>.W <account id>.L <item name>.?B (CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO)
|
||||||
|
void clif_parse_sale_search( int fd, struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
char item_name[ITEM_NAME_LENGTH];
|
||||||
|
struct item_data *id = NULL;
|
||||||
|
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 4) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pc_has_permission( sd, PC_PERM_CASHSHOP_SALE ) ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
safestrncpy( item_name, RFIFOCP(fd, 8), min(RFIFOW(fd, 2) - 7, ITEM_NAME_LENGTH) );
|
||||||
|
|
||||||
|
id = itemdb_searchname(item_name);
|
||||||
|
|
||||||
|
if( id ){
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < cash_shop_items[CASHSHOP_TAB_SALE].count; i++ ){
|
||||||
|
if( cash_shop_items[CASHSHOP_TAB_SALE].item[i]->nameid == id->nameid ){
|
||||||
|
clif_sale_search_reply( sd, cash_shop_items[CASHSHOP_TAB_SALE].item[i] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found
|
||||||
|
clif_sale_search_reply( sd, NULL );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reply if an item was successfully put on sale or not.
|
||||||
|
/// 09af <result>.W (ZC_ACK_APPLY_BARGAIN_SALE_ITEM)
|
||||||
|
void clif_sale_add_reply( struct map_session_data* sd, enum e_sale_add_result result ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int fd = sd->fd;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 4);
|
||||||
|
WFIFOW(fd, 0) = 0x9af;
|
||||||
|
WFIFOW(fd, 2) = (uint16)result;
|
||||||
|
WFIFOSET(fd, 4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A client request to put an item on sale.
|
||||||
|
/// 09ae <account id>.L <item id>.W <amount>.L <start time>.L <hours on sale>.B (CZ_REQ_APPLY_BARGAIN_SALE_ITEM)
|
||||||
|
void clif_parse_sale_add( int fd, struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int32 count;
|
||||||
|
int16 nameid;
|
||||||
|
int startTime;
|
||||||
|
int endTime;
|
||||||
|
uint8 sellingHours;
|
||||||
|
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 2) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pc_has_permission( sd, PC_PERM_CASHSHOP_SALE ) ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameid = RFIFOW(fd, 6);
|
||||||
|
count = RFIFOL(fd, 8);
|
||||||
|
startTime = RFIFOL(fd, 12);
|
||||||
|
sellingHours = RFIFOB(fd, 16);
|
||||||
|
endTime = startTime + sellingHours * 60 * 60;
|
||||||
|
|
||||||
|
clif_sale_add_reply( sd, sale_add_item(nameid,count,startTime,endTime) );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reply to an item removal from sale.
|
||||||
|
/// 09b1 <result>.W (ZC_ACK_REMOVE_BARGAIN_SALE_ITEM)
|
||||||
|
void clif_sale_remove_reply( struct map_session_data* sd, bool failed ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
int fd = sd->fd;
|
||||||
|
|
||||||
|
WFIFOHEAD(fd, 4);
|
||||||
|
WFIFOW(fd, 0) = 0x9b1;
|
||||||
|
WFIFOW(fd, 2) = failed;
|
||||||
|
WFIFOSET(fd, 4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request to remove an item from sale.
|
||||||
|
/// 09b0 <account id>.L <item id>.W (CZ_REQ_REMOVE_BARGAIN_SALE_ITEM)
|
||||||
|
void clif_parse_sale_remove( int fd, struct map_session_data* sd ){
|
||||||
|
#if PACKETVER_SUPPORTS_SALES
|
||||||
|
nullpo_retv(sd);
|
||||||
|
|
||||||
|
if( RFIFOL(fd, 2) != sd->status.account_id ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pc_has_permission( sd, PC_PERM_CASHSHOP_SALE ) ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clif_sale_remove_reply(sd, !sale_remove_item(RFIFOW(fd, 6)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*==========================================
|
/*==========================================
|
||||||
* Main client packet processing function
|
* Main client packet processing function
|
||||||
*------------------------------------------*/
|
*------------------------------------------*/
|
||||||
@ -19258,10 +19516,10 @@ void packetdb_readdb(bool reload)
|
|||||||
//#0x0980
|
//#0x0980
|
||||||
7, 0, 0, 29, 28, 0, 0, 0, 6, 2, -1, 0, 0, -1, -1, 0,
|
7, 0, 0, 29, 28, 0, 0, 0, 6, 2, -1, 0, 0, -1, -1, 0,
|
||||||
31, 0, 0, 0, 0, 0, 0, -1, 8, 11, 9, 8, 0, 0, 0, 22,
|
31, 0, 0, 0, 0, 0, 0, -1, 8, 11, 9, 8, 0, 0, 0, 22,
|
||||||
0, 0, 0, 0, 0, 0, 12, 10, 14, 10, 14, 6, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 12, 10, 14, 10, 14, 6, -1, 8, 17, 4,
|
||||||
0, 0, 0, 0, 0, 0, 6, 4, 6, 4, 0, 0, 0, 0, 0, 0,
|
8, 4, 8, 4, 6, 0, 6, 4, 6, 4, 0, 0, 6, 0, 0, 0,
|
||||||
//#0x09C0
|
//#0x09C0
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 17, 0, 0,102, 0,
|
0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 23, 17, 0, 0,102, 0,
|
||||||
0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, -1, -1, -1, 0, 7,
|
0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, -1, -1, -1, 0, 7,
|
||||||
0, 0, 0, 0, 0, 18, 22, 3, 11, 0, 11, -1, 0, 3, 11, 0,
|
0, 0, 0, 0, 0, 18, 22, 3, 11, 0, 11, -1, 0, 3, 11, 0,
|
||||||
0, 11, 12, 11, 0, 0, 0, 75, -1,143, 0, 0, 0, -1, -1, -1,
|
0, 11, 12, 11, 0, 0, 0, 75, -1,143, 0, 0, 0, -1, -1, -1,
|
||||||
@ -19517,6 +19775,13 @@ void packetdb_readdb(bool reload)
|
|||||||
{ clif_parse_SelectCart, "selectcart" },
|
{ clif_parse_SelectCart, "selectcart" },
|
||||||
// Clan System
|
// Clan System
|
||||||
{ clif_parse_clan_chat, "clanchat" },
|
{ clif_parse_clan_chat, "clanchat" },
|
||||||
|
// Sale
|
||||||
|
{ clif_parse_sale_search, "salesearch" },
|
||||||
|
{ clif_parse_sale_add, "saleadd" },
|
||||||
|
{ clif_parse_sale_remove, "saleremove" },
|
||||||
|
{ clif_parse_sale_open, "saleopen" },
|
||||||
|
{ clif_parse_sale_close, "saleclose" },
|
||||||
|
{ clif_parse_sale_refresh, "salerefresh" },
|
||||||
{NULL,NULL}
|
{NULL,NULL}
|
||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
|
@ -31,6 +31,7 @@ struct battleground_data;
|
|||||||
struct quest;
|
struct quest;
|
||||||
struct party_booking_ad_info;
|
struct party_booking_ad_info;
|
||||||
enum e_party_member_withdraw;
|
enum e_party_member_withdraw;
|
||||||
|
struct sale_item_data;
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
enum { // packet DB
|
enum { // packet DB
|
||||||
@ -983,6 +984,11 @@ void clif_clan_message(struct clan *clan,const char *mes,int len);
|
|||||||
void clif_clan_onlinecount( struct clan* clan );
|
void clif_clan_onlinecount( struct clan* clan );
|
||||||
void clif_clan_leave( struct map_session_data* sd );
|
void clif_clan_leave( struct map_session_data* sd );
|
||||||
|
|
||||||
|
// Bargain Tool
|
||||||
|
void clif_sale_start(struct sale_item_data* sale_item, struct block_list* bl, enum send_target target);
|
||||||
|
void clif_sale_end(struct sale_item_data* sale_item, struct block_list* bl, enum send_target target);
|
||||||
|
void clif_sale_amount(struct sale_item_data* sale_item, struct block_list* bl, enum send_target target);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Color Table
|
* Color Table
|
||||||
**/
|
**/
|
||||||
|
@ -74,6 +74,7 @@ char mob2_table[32] = "mob_db2";
|
|||||||
char mob_skill_table[32] = "mob_skill_db";
|
char mob_skill_table[32] = "mob_skill_db";
|
||||||
char mob_skill2_table[32] = "mob_skill_db2";
|
char mob_skill2_table[32] = "mob_skill_db2";
|
||||||
#endif
|
#endif
|
||||||
|
char sales_table[32] = "sales";
|
||||||
char vendings_table[32] = "vendings";
|
char vendings_table[32] = "vendings";
|
||||||
char vending_items_table[32] = "vending_items";
|
char vending_items_table[32] = "vending_items";
|
||||||
char market_table[32] = "market";
|
char market_table[32] = "market";
|
||||||
@ -4022,6 +4023,8 @@ int inter_config_read(char *cfgName)
|
|||||||
strcpy(roulette_table, w2);
|
strcpy(roulette_table, w2);
|
||||||
else if (strcmpi(w1, "market_table") == 0)
|
else if (strcmpi(w1, "market_table") == 0)
|
||||||
strcpy(market_table, w2);
|
strcpy(market_table, w2);
|
||||||
|
else if (strcmpi(w1, "sales_table") == 0)
|
||||||
|
strcpy(sales_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)
|
||||||
|
@ -49,6 +49,7 @@ enum e_pc_permission {
|
|||||||
PC_PERM_ENABLE_COMMAND = 0x01000000,
|
PC_PERM_ENABLE_COMMAND = 0x01000000,
|
||||||
PC_PERM_BYPASS_STAT_ONCLONE = 0x02000000,
|
PC_PERM_BYPASS_STAT_ONCLONE = 0x02000000,
|
||||||
PC_PERM_BYPASS_MAX_STAT = 0x04000000,
|
PC_PERM_BYPASS_MAX_STAT = 0x04000000,
|
||||||
|
PC_PERM_CASHSHOP_SALE = 0x08000000,
|
||||||
//.. add other here
|
//.. add other here
|
||||||
PC_PERM_ALLPERMISSION = 0xFFFFFFFF,
|
PC_PERM_ALLPERMISSION = 0xFFFFFFFF,
|
||||||
};
|
};
|
||||||
@ -84,6 +85,7 @@ static const struct {
|
|||||||
{ "command_enable",PC_PERM_ENABLE_COMMAND },
|
{ "command_enable",PC_PERM_ENABLE_COMMAND },
|
||||||
{ "bypass_stat_onclone",PC_PERM_BYPASS_STAT_ONCLONE },
|
{ "bypass_stat_onclone",PC_PERM_BYPASS_STAT_ONCLONE },
|
||||||
{ "bypass_max_stat",PC_PERM_BYPASS_MAX_STAT },
|
{ "bypass_max_stat",PC_PERM_BYPASS_MAX_STAT },
|
||||||
|
{ "cashshop_sale", PC_PERM_CASHSHOP_SALE },
|
||||||
{ "all_permission", PC_PERM_ALLPERMISSION },
|
{ "all_permission", PC_PERM_ALLPERMISSION },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user