rathena/src/map/cashshop.c
Cydh Ramdh e1b658e462 * Item GUID Updates:
* Removed `ENABLE_ITEM_GUID` from src/config/core.h, after long time introduction it's worth to be enabled by default. Just remove all items with GUID flag if want to remove it generates GUID .
  * Implemented client interface to merge the items. Packet struct credits: 856b6f1feb
  * Edited `mergeitem` script command to use the interface, moved the 'old' `mergeitem` to `mergeitem2`.
  * Fixed the behavior to add GUID'd item to inventory in Cash Shop.
  * Updated item_flag.txt for listing `IT_CASH` items (item type 18).
  * NOTE: `IT_CASH` items which defined in item_flag.txt with flag `4`, cannot be merged.
  * NOTE: Please check the packets for older clients. Tested work on 2012-04-10, 2013-08-07 and 2014-10-22.
  * NOTE: Please import 'upgrade_20150804.sql' for MySQL Log Database.
* Removed duplicate clif functions:
  * `clif_msgtable()` -> `clif_msg()`
  * `clif_msgtable_num()` -> `clif_msg_value()`
* Item DB Updates:
  * Indonesia_Box2 (17079) should gives 5x Wing_Of_Butterfly (602).
  * Changed some item types to 18.

Signed-off-by: Cydh Ramdh <cydh@pservero.com>
2015-08-04 20:57:49 +07:00

346 lines
9.0 KiB
C

// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#include "../common/cbasetypes.h" // uint16, uint32
#include "../common/malloc.h" // CREATE, RECREATE, aFree
#include "../common/showmsg.h" // ShowWarning, ShowStatus
#include "cashshop.h"
#include "pet.h" // pet_create_egg
#include <string.h> // memset
#include <stdlib.h> // atoi
struct cash_item_db cash_shop_items[CASHSHOP_TAB_SEARCH];
bool cash_shop_defined = false;
extern char item_cash_db_db[32];
extern char item_cash_db2_db[32];
/*
* Reads one line from database and assigns it to RAM.
* return
* 0 = failure
* 1 = success
*/
static int cashshop_parse_dbrow( char** str, const char* source, int line ){
unsigned short nameid = atoi( str[1] );
if( itemdb_exists( nameid ) ){
uint16 tab = atoi( str[0] );
uint32 price = atoi( str[2] );
struct cash_item_data* cid;
int j;
if( tab > CASHSHOP_TAB_SEARCH ){
ShowWarning( "cashshop_parse_dbrow: Invalid tab %d in line %d of \"%s\", skipping...\n", tab, line, source );
return 0;
}else if( price < 1 ){
ShowWarning( "cashshop_parse_dbrow: Invalid price %d in line %d of \"%s\", skipping...\n", price, line, source );
return 0;
}
ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid );
if( j == cash_shop_items[tab].count ){
RECREATE( cash_shop_items[tab].item, struct cash_item_data *, ++cash_shop_items[tab].count );
CREATE( cash_shop_items[tab].item[ cash_shop_items[tab].count - 1], struct cash_item_data, 1 );
cid = cash_shop_items[tab].item[ cash_shop_items[tab].count - 1];
}else{
cid = cash_shop_items[tab].item[j];
}
cid->nameid = nameid;
cid->price = price;
cash_shop_defined = true;
return 1;
}else{
ShowWarning( "cashshop_parse_dbrow: Invalid ID %hu in line %d of \"%s\", skipping...\n", nameid, line, source );
}
return 0;
}
/*
* Reads database from TXT format,
* parses lines and sends them to parse_dbrow.
* TODO: Change to sv_readdb
*/
static void cashshop_read_db_txt( void ){
const char* filename[] = { DBPATH"item_cash_db.txt", DBIMPORT"/item_cash_db.txt" };
int fi;
for( fi = 0; fi < ARRAYLENGTH( filename ); ++fi ){
uint32 lines = 0, count = 0;
char line[1024];
char path[256];
FILE* fp;
sprintf( path, "%s/%s", db_path, filename[fi] );
fp = fopen( path, "r" );
if( fp == NULL ) {
ShowWarning( "itemdb_readdb: File not found \"%s\", skipping.\n", path );
continue;
}
while( fgets( line, sizeof( line ), fp ) ){
char *str[3], *p;
int i;
lines++;
if( line[0] == '/' && line[1] == '/' )
continue;
memset( str, 0, sizeof( str ) );
p = line;
while( ISSPACE( *p ) )
++p;
if( *p == '\0' )
continue;
for( i = 0; i < 2; ++i ){
str[i] = p;
p = strchr( p, ',' );
if( p == NULL )
break;
*p = '\0';
++p;
}
str[2] = p;
while( !ISSPACE( *p ) && *p != '\0' && *p != '/' )
++p;
if( p == NULL ){
ShowError("cashshop_read_db_txt: Insufficient columns in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi( str[0] ) );
continue;
}
if( !cashshop_parse_dbrow( str, path, lines ) )
continue;
count++;
}
fclose(fp);
ShowStatus( "Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, path );
}
}
/*
* Reads database from SQL format,
* parses line and sends them to parse_dbrow.
*/
static int cashshop_read_db_sql( void ){
const char* cash_db_name[] = { item_cash_db_db, item_cash_db2_db };
int fi;
for( fi = 0; fi < ARRAYLENGTH( cash_db_name ); ++fi ){
uint32 lines = 0, count = 0;
if( SQL_ERROR == Sql_Query( mmysql_handle, "SELECT `tab`, `item_id`, `price` FROM `%s`", cash_db_name[fi] ) ){
Sql_ShowDebug( mmysql_handle );
continue;
}
while( SQL_SUCCESS == Sql_NextRow( mmysql_handle ) ){
char* str[3];
int i;
++lines;
for( i = 0; i < 3; ++i ){
Sql_GetData( mmysql_handle, i, &str[i], NULL );
if( str[i] == NULL ){
str[i] = "";
}
}
if( !cashshop_parse_dbrow( str, cash_db_name[fi], lines ) )
continue;
++count;
}
Sql_FreeResult( mmysql_handle );
ShowStatus( "Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, cash_db_name[fi] );
}
return 0;
}
/*
* Determines whether to read TXT or SQL database
* based on 'db_use_sqldbs' in conf/map_athena.conf.
*/
static void cashshop_read_db( void ){
if( db_use_sqldbs ){
cashshop_read_db_sql();
} else {
cashshop_read_db_txt();
}
}
/** Attempts to purchase a cashshop item from the list.
* Checks if the transaction is valid and if the user has enough inventory space to receive the item.
* If yes, take cashpoints and give items; else return clif_error.
* @param sd Player that request to buy item(s)
* @param kafrapoints
* @param n Count of item list
* @param item_list Array of item ID
* @return true: success, false: fail
*/
bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ){
uint32 totalcash = 0;
uint32 totalweight = 0;
int i,new_;
if( sd == NULL || item_list == NULL || !cash_shop_defined){
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_UNKNOWN );
return false;
}else if( sd->state.trading ){
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_PC_STATE );
return false;
}
new_ = 0;
for( i = 0; i < n; ++i ){
unsigned short nameid = *( item_list + i * 5 );
uint32 quantity = *( item_list + i * 5 + 2 );
uint16 tab = *( item_list + i * 5 + 4 );
int j;
if( tab > CASHSHOP_TAB_SEARCH ){
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
return false;
}
ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid || nameid == itemdb_viewid(cash_shop_items[tab].item[j]->nameid) );
nameid = *( item_list + i * 5 ) = cash_shop_items[tab].item[j]->nameid; //item_avail replacement
if( j == cash_shop_items[tab].count || !itemdb_exists( nameid ) ){
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKONWN_ITEM );
return false;
}else if( !itemdb_isstackable( nameid ) && quantity > 1 ){
/* ShowWarning( "Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable cash item %hu!\n", sd->status.name, sd->status.account_id, sd->status.char_id, quantity, nameid ); */
quantity = *( item_list + i * 5 + 2 ) = 1;
}
switch( pc_checkadditem( sd, nameid, quantity ) ){
case CHKADDITEM_EXIST:
break;
case CHKADDITEM_NEW:
new_++;
break;
case CHKADDITEM_OVERAMOUNT:
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_OVER_PRODUCT_TOTAL_CNT );
return false;
}
totalcash += cash_shop_items[tab].item[j]->price * quantity;
totalweight += itemdb_weight( nameid ) * quantity;
}
if( ( totalweight + sd->weight ) > sd->max_weight ){
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_INVENTORY_WEIGHT );
return false;
}else if( pc_inventoryblank( sd ) < new_ ){
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_INVENTORY_ITEMCNT );
return false;
}
if(pc_paycash( sd, totalcash, kafrapoints, LOG_TYPE_CASH ) < 0){
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_SHORTTAGE_CASH );
return false;
}
for( i = 0; i < n; ++i ){
unsigned short nameid = *( item_list + i * 5 );
uint32 quantity = *( item_list + i * 5 + 2 );
struct item_data *id = itemdb_search(nameid);
if (!id)
continue;
if (!itemdb_isstackable2(id) && quantity > 1)
quantity = 1;
if (!pet_create_egg(sd, nameid)) {
unsigned short get_amt = quantity, j;
if (id->flag.guid)
get_amt = 1;
for (j = 0; j < quantity; j += get_amt) {
struct item item_tmp = { 0 };
item_tmp.nameid = nameid;
item_tmp.identify = 1;
switch( pc_additem( sd, &item_tmp, get_amt, LOG_TYPE_CASH ) ){
case ADDITEM_OVERWEIGHT:
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_INVENTORY_WEIGHT );
return false;
case ADDITEM_OVERITEM:
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_INVENTORY_ITEMCNT );
return false;
case ADDITEM_OVERAMOUNT:
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_OVER_PRODUCT_TOTAL_CNT );
return false;
case ADDITEM_STACKLIMIT:
clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_RUNE_OVERCOUNT );
return false;
}
}
}
}
clif_cashshop_result( sd, 0, CASHSHOP_RESULT_SUCCESS ); //Doesn't show any message?
return true;
}
/*
* Reloads cashshop database by destroying it and reading it again.
*/
void cashshop_reloaddb( void ){
do_final_cashshop();
do_init_cashshop();
}
/*
* Destroys cashshop class.
* Closes all and cleanup.
*/
void do_final_cashshop( void ){
int tab, i;
for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_SEARCH; tab++ ){
for( i = 0; i < cash_shop_items[tab].count; i++ ){
aFree( cash_shop_items[tab].item[i] );
}
aFree( cash_shop_items[tab].item );
}
memset( cash_shop_items, 0, sizeof( cash_shop_items ) );
}
/*
* Initializes cashshop class.
* return
* 0 : success
*/
void do_init_cashshop( void ){
cash_shop_defined = false;
cashshop_read_db();
}