cashshop.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "cashshop.hpp"
  4. #include <stdlib.h> // atoi
  5. #include <string.h> // memset
  6. #include "../common/cbasetypes.hpp" // uint16, uint32
  7. #include "../common/malloc.hpp" // CREATE, RECREATE, aFree
  8. #include "../common/showmsg.hpp" // ShowWarning, ShowStatus
  9. #include "clif.hpp"
  10. #include "log.hpp"
  11. #include "pc.hpp" // s_map_session_data
  12. #include "pet.hpp" // pet_create_egg
  13. struct cash_item_db cash_shop_items[CASHSHOP_TAB_MAX];
  14. #if PACKETVER_SUPPORTS_SALES
  15. struct sale_item_db sale_items;
  16. #endif
  17. bool cash_shop_defined = false;
  18. extern char item_cash_table[32];
  19. extern char item_cash2_table[32];
  20. extern char sales_table[32];
  21. /*
  22. * Reads one line from database and assigns it to RAM.
  23. * return
  24. * 0 = failure
  25. * 1 = success
  26. */
  27. static bool cashshop_parse_dbrow(char* fields[], int columns, int current) {
  28. uint16 tab = atoi(fields[0]);
  29. t_itemid nameid = strtoul(fields[1], nullptr, 10);
  30. uint32 price = atoi(fields[2]);
  31. int j;
  32. struct cash_item_data* cid;
  33. if( !itemdb_exists( nameid ) ){
  34. ShowWarning( "cashshop_parse_dbrow: Invalid ID %u in line '%d', skipping...\n", nameid, current );
  35. return 0;
  36. }
  37. if( tab >= CASHSHOP_TAB_MAX ){
  38. ShowWarning( "cashshop_parse_dbrow: Invalid tab %d in line '%d', skipping...\n", tab, current );
  39. return 0;
  40. }else if( price < 1 ){
  41. ShowWarning( "cashshop_parse_dbrow: Invalid price %d in line '%d', skipping...\n", price, current );
  42. return 0;
  43. }
  44. ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid );
  45. if( j == cash_shop_items[tab].count ){
  46. RECREATE( cash_shop_items[tab].item, struct cash_item_data *, ++cash_shop_items[tab].count );
  47. CREATE( cash_shop_items[tab].item[ cash_shop_items[tab].count - 1], struct cash_item_data, 1 );
  48. cid = cash_shop_items[tab].item[ cash_shop_items[tab].count - 1];
  49. }else{
  50. cid = cash_shop_items[tab].item[j];
  51. }
  52. cid->nameid = nameid;
  53. cid->price = price;
  54. cash_shop_defined = true;
  55. return 1;
  56. }
  57. /*
  58. * Reads database from TXT format,
  59. * parses lines and sends them to parse_dbrow.
  60. */
  61. static void cashshop_read_db_txt( void ){
  62. const char* dbsubpath[] = {
  63. "",
  64. "/" DBIMPORT,
  65. };
  66. int fi;
  67. for( fi = 0; fi < ARRAYLENGTH( dbsubpath ); ++fi ){
  68. uint8 n1 = (uint8)(strlen(db_path)+strlen(dbsubpath[fi])+1);
  69. uint8 n2 = (uint8)(strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[fi])+1);
  70. char* dbsubpath1 = (char*)aMalloc(n1+1);
  71. char* dbsubpath2 = (char*)aMalloc(n2+1);
  72. if(fi==0) {
  73. safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[fi]);
  74. safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[fi]);
  75. }
  76. else {
  77. safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[fi]);
  78. safesnprintf(dbsubpath2,n1,"%s%s",db_path,dbsubpath[fi]);
  79. }
  80. sv_readdb(dbsubpath2, "item_cash_db.txt", ',', 3, 3, -1, &cashshop_parse_dbrow, fi > 0);
  81. aFree(dbsubpath1);
  82. aFree(dbsubpath2);
  83. }
  84. }
  85. /*
  86. * Reads database from SQL format,
  87. * parses line and sends them to parse_dbrow.
  88. */
  89. static int cashshop_read_db_sql( void ){
  90. const char* cash_db_name[] = { item_cash_table, item_cash2_table };
  91. int fi;
  92. for( fi = 0; fi < ARRAYLENGTH( cash_db_name ); ++fi ){
  93. uint32 lines = 0, count = 0;
  94. if( SQL_ERROR == Sql_Query( mmysql_handle, "SELECT `tab`, `item_id`, `price` FROM `%s`", cash_db_name[fi] ) ){
  95. Sql_ShowDebug( mmysql_handle );
  96. continue;
  97. }
  98. while( SQL_SUCCESS == Sql_NextRow( mmysql_handle ) ){
  99. char* str[3];
  100. char dummy[256] = "";
  101. int i;
  102. ++lines;
  103. for( i = 0; i < 3; ++i ){
  104. Sql_GetData( mmysql_handle, i, &str[i], NULL );
  105. if( str[i] == NULL ){
  106. str[i] = dummy;
  107. }
  108. }
  109. if( !cashshop_parse_dbrow( str, 3, lines ) ) {
  110. ShowError("cashshop_read_db_sql: Cannot process table '%s' at line '%d', skipping...\n", cash_db_name[fi], lines);
  111. continue;
  112. }
  113. ++count;
  114. }
  115. Sql_FreeResult( mmysql_handle );
  116. ShowStatus( "Done reading '" CL_WHITE "%u" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, cash_db_name[fi] );
  117. }
  118. return 0;
  119. }
  120. #if PACKETVER_SUPPORTS_SALES
  121. static bool sale_parse_dbrow( char* fields[], int columns, int current ){
  122. t_itemid nameid = strtoul(fields[0], nullptr, 10);
  123. int start = atoi(fields[1]), end = atoi(fields[2]), amount = atoi(fields[3]), i;
  124. time_t now = time(NULL);
  125. struct sale_item_data* sale_item = NULL;
  126. if( !itemdb_exists(nameid) ){
  127. ShowWarning( "sale_parse_dbrow: Invalid ID %u in line '%d', skipping...\n", nameid, current );
  128. return false;
  129. }
  130. ARR_FIND( 0, cash_shop_items[CASHSHOP_TAB_SALE].count, i, cash_shop_items[CASHSHOP_TAB_SALE].item[i]->nameid == nameid );
  131. if( i == cash_shop_items[CASHSHOP_TAB_SALE].count ){
  132. ShowWarning( "sale_parse_dbrow: ID %u is not registered in the limited tab in line '%d', skipping...\n", nameid, current );
  133. return false;
  134. }
  135. // Check if the end is after the start
  136. if( start >= end ){
  137. ShowWarning( "sale_parse_dbrow: Sale for item %u was ignored, because the timespan was not correct.\n", nameid );
  138. return false;
  139. }
  140. // Check if it is already in the past
  141. if( end < now ){
  142. ShowWarning( "sale_parse_dbrow: An outdated sale for item %u was ignored.\n", nameid );
  143. return false;
  144. }
  145. // Check if there is already an entry
  146. sale_item = sale_find_item(nameid,false);
  147. if( sale_item == NULL ){
  148. RECREATE(sale_items.item, struct sale_item_data *, ++sale_items.count);
  149. CREATE(sale_items.item[sale_items.count - 1], struct sale_item_data, 1);
  150. sale_item = sale_items.item[sale_items.count - 1];
  151. }
  152. sale_item->nameid = nameid;
  153. sale_item->start = start;
  154. sale_item->end = end;
  155. sale_item->amount = amount;
  156. sale_item->timer_start = INVALID_TIMER;
  157. sale_item->timer_end = INVALID_TIMER;
  158. return true;
  159. }
  160. static void sale_read_db_sql( void ){
  161. uint32 lines = 0, count = 0;
  162. if( SQL_ERROR == Sql_Query( mmysql_handle, "SELECT `nameid`, UNIX_TIMESTAMP(`start`), UNIX_TIMESTAMP(`end`), `amount` FROM `%s` WHERE `end` > now()", sales_table ) ){
  163. Sql_ShowDebug(mmysql_handle);
  164. return;
  165. }
  166. while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ){
  167. char* str[4];
  168. char dummy[256] = "";
  169. int i;
  170. lines++;
  171. for( i = 0; i < 4; i++ ){
  172. Sql_GetData( mmysql_handle, i, &str[i], NULL );
  173. if( str[i] == NULL ){
  174. str[i] = dummy;
  175. }
  176. }
  177. if( !sale_parse_dbrow( str, 4, lines ) ){
  178. ShowError( "sale_read_db_sql: Cannot process table '%s' at line '%d', skipping...\n", sales_table, lines );
  179. continue;
  180. }
  181. count++;
  182. }
  183. Sql_FreeResult(mmysql_handle);
  184. ShowStatus( "Done reading '" CL_WHITE "%u" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, sales_table );
  185. }
  186. static TIMER_FUNC(sale_end_timer){
  187. struct sale_item_data* sale_item = (struct sale_item_data*)data;
  188. // Remove the timer so the sale end is not sent out again
  189. delete_timer( sale_item->timer_end, sale_end_timer );
  190. sale_item->timer_end = INVALID_TIMER;
  191. clif_sale_end( sale_item, NULL, ALL_CLIENT );
  192. sale_remove_item( sale_item->nameid );
  193. return 1;
  194. }
  195. static TIMER_FUNC(sale_start_timer){
  196. struct sale_item_data* sale_item = (struct sale_item_data*)data;
  197. clif_sale_start( sale_item, NULL, ALL_CLIENT );
  198. clif_sale_amount( sale_item, NULL, ALL_CLIENT );
  199. // Clear the start timer
  200. if( sale_item->timer_start != INVALID_TIMER ){
  201. delete_timer( sale_item->timer_start, sale_start_timer );
  202. sale_item->timer_start = INVALID_TIMER;
  203. }
  204. // Init sale end
  205. sale_item->timer_end = add_timer( gettick() + (unsigned int)( sale_item->end - time(NULL) ) * 1000, sale_end_timer, 0, (intptr_t)sale_item );
  206. return 1;
  207. }
  208. enum e_sale_add_result sale_add_item( t_itemid nameid, int32 count, time_t from, time_t to ){
  209. int i;
  210. struct sale_item_data* sale_item;
  211. // Check if the item exists in the sales tab
  212. ARR_FIND( 0, cash_shop_items[CASHSHOP_TAB_SALE].count, i, cash_shop_items[CASHSHOP_TAB_SALE].item[i]->nameid == nameid );
  213. // Item does not exist in the sales tab
  214. if( i == cash_shop_items[CASHSHOP_TAB_SALE].count ){
  215. return SALE_ADD_FAILED;
  216. }
  217. // Adding a sale in the past is not possible
  218. if( from < time(NULL) ){
  219. return SALE_ADD_FAILED;
  220. }
  221. // The end has to be after the start
  222. if( from >= to ){
  223. return SALE_ADD_FAILED;
  224. }
  225. // Amount has to be positive - this should be limited from the client too
  226. if( count == 0 ){
  227. return SALE_ADD_FAILED;
  228. }
  229. // Check if a sale of this item already exists
  230. if( sale_find_item(nameid, false) ){
  231. return SALE_ADD_DUPLICATE;
  232. }
  233. if( SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`nameid`,`start`,`end`,`amount`) VALUES ( '%u', FROM_UNIXTIME(%d), FROM_UNIXTIME(%d), '%d' )", sales_table, nameid, (uint32)from, (uint32)to, count) ){
  234. Sql_ShowDebug(mmysql_handle);
  235. return SALE_ADD_FAILED;
  236. }
  237. RECREATE(sale_items.item, struct sale_item_data *, ++sale_items.count);
  238. CREATE(sale_items.item[sale_items.count - 1], struct sale_item_data, 1);
  239. sale_item = sale_items.item[sale_items.count - 1];
  240. sale_item->nameid = nameid;
  241. sale_item->start = from;
  242. sale_item->end = to;
  243. sale_item->amount = count;
  244. sale_item->timer_start = add_timer( gettick() + (unsigned int)(from - time(NULL)) * 1000, sale_start_timer, 0, (intptr_t)sale_item );
  245. sale_item->timer_end = INVALID_TIMER;
  246. return SALE_ADD_SUCCESS;
  247. }
  248. bool sale_remove_item( t_itemid nameid ){
  249. struct sale_item_data* sale_item;
  250. int i;
  251. // Check if there is an entry for this item id
  252. sale_item = sale_find_item(nameid, false);
  253. if( sale_item == NULL ){
  254. return false;
  255. }
  256. // Delete it from the database
  257. if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `nameid` = '%u'", sales_table, nameid ) ){
  258. Sql_ShowDebug(mmysql_handle);
  259. return false;
  260. }
  261. if( sale_item->timer_start != INVALID_TIMER ){
  262. delete_timer(sale_item->timer_start, sale_start_timer);
  263. sale_item->timer_start = INVALID_TIMER;
  264. }
  265. // Check if the sale is currently running
  266. if( sale_item->timer_end != INVALID_TIMER ){
  267. delete_timer(sale_item->timer_end, sale_end_timer);
  268. sale_item->timer_end = INVALID_TIMER;
  269. // Notify all clients that the sale has ended
  270. clif_sale_end(sale_item, NULL, ALL_CLIENT);
  271. }
  272. // Find the original pointer in the array
  273. ARR_FIND( 0, sale_items.count, i, sale_items.item[i] == sale_item );
  274. // Is there still any entry left?
  275. if( --sale_items.count > 0 ){
  276. // fill the hole by moving the rest
  277. for( ; i < sale_items.count; i++ ){
  278. memcpy( sale_items.item[i], sale_items.item[i + 1], sizeof(struct sale_item_data) );
  279. }
  280. aFree(sale_items.item[i]);
  281. RECREATE(sale_items.item, struct sale_item_data *, sale_items.count);
  282. }else{
  283. aFree(sale_items.item[0]);
  284. aFree(sale_items.item);
  285. sale_items.item = NULL;
  286. }
  287. return true;
  288. }
  289. struct sale_item_data* sale_find_item( t_itemid nameid, bool onsale ){
  290. int i;
  291. struct sale_item_data* sale_item;
  292. time_t now = time(NULL);
  293. ARR_FIND( 0, sale_items.count, i, sale_items.item[i]->nameid == nameid );
  294. // No item with the specified item id was found
  295. if( i == sale_items.count ){
  296. return NULL;
  297. }
  298. sale_item = sale_items.item[i];
  299. // No need to check any further
  300. if( !onsale ){
  301. return sale_item;
  302. }
  303. // The sale is in the future
  304. if( sale_items.item[i]->start > now ){
  305. return NULL;
  306. }
  307. // The sale was in the past
  308. if( sale_items.item[i]->end < now ){
  309. return NULL;
  310. }
  311. // The amount has been used up already
  312. if( sale_items.item[i]->amount == 0 ){
  313. return NULL;
  314. }
  315. // Return the sale item
  316. return sale_items.item[i];
  317. }
  318. void sale_notify_login( struct map_session_data* sd ){
  319. int i;
  320. for( i = 0; i < sale_items.count; i++ ){
  321. if( sale_items.item[i]->timer_end != INVALID_TIMER ){
  322. clif_sale_start( sale_items.item[i], &sd->bl, SELF );
  323. clif_sale_amount( sale_items.item[i], &sd->bl, SELF );
  324. }
  325. }
  326. }
  327. #endif
  328. /*
  329. * Determines whether to read TXT or SQL database
  330. * based on 'db_use_sqldbs' in conf/map_athena.conf.
  331. */
  332. static void cashshop_read_db( void ){
  333. #if PACKETVER_SUPPORTS_SALES
  334. int i;
  335. time_t now = time(NULL);
  336. #endif
  337. if( db_use_sqldbs ){
  338. cashshop_read_db_sql();
  339. } else {
  340. cashshop_read_db_txt();
  341. }
  342. #if PACKETVER_SUPPORTS_SALES
  343. sale_read_db_sql();
  344. // Clean outdated sales
  345. if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `end` < FROM_UNIXTIME(%d)", sales_table, (uint32)now ) ){
  346. Sql_ShowDebug(mmysql_handle);
  347. }
  348. // Init next sale start, if there is any
  349. for( i = 0; i < sale_items.count; i++ ){
  350. struct sale_item_data* it = sale_items.item[i];
  351. if( it->start > now ){
  352. it->timer_start = add_timer( gettick() + (unsigned int)( it->start - time(NULL) ) * 1000, sale_start_timer, 0, (intptr_t)it );
  353. }else{
  354. sale_start_timer( 0, gettick(), 0, (intptr_t)it );
  355. }
  356. }
  357. #endif
  358. }
  359. /** Attempts to purchase a cashshop item from the list.
  360. * Checks if the transaction is valid and if the user has enough inventory space to receive the item.
  361. * If yes, take cashpoints and give items; else return clif_error.
  362. * @param sd Player that request to buy item(s)
  363. * @param kafrapoints
  364. * @param n Count of item list
  365. * @param item_list Array of item ID
  366. * @return true: success, false: fail
  367. */
  368. bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ){
  369. uint32 totalcash = 0;
  370. uint32 totalweight = 0;
  371. int i,new_;
  372. if( sd == NULL || item_list == NULL || !cash_shop_defined){
  373. clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_UNKNOWN );
  374. return false;
  375. }else if( sd->state.trading ){
  376. clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_PC_STATE );
  377. return false;
  378. }
  379. new_ = 0;
  380. for( i = 0; i < n; ++i ){
  381. t_itemid nameid = item_list[i].itemId;
  382. uint32 quantity = item_list[i].amount;
  383. uint16 tab = item_list[i].tab;
  384. int j;
  385. if( tab >= CASHSHOP_TAB_MAX ){
  386. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  387. return false;
  388. }
  389. 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) );
  390. if( j == cash_shop_items[tab].count ){
  391. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKONWN_ITEM );
  392. return false;
  393. }
  394. nameid = item_list[i].itemId = cash_shop_items[tab].item[j]->nameid; //item_avail replacement
  395. std::shared_ptr<item_data> id = item_db.find(nameid);
  396. if( !id ){
  397. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKONWN_ITEM );
  398. return false;
  399. }
  400. if( quantity > 99 ){
  401. // 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
  402. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  403. return false;
  404. }
  405. #if PACKETVER_SUPPORTS_SALES
  406. if( tab == CASHSHOP_TAB_SALE ){
  407. struct sale_item_data* sale = sale_find_item( nameid, true );
  408. if( sale == NULL ){
  409. // Client tried to buy an item from sale that was not even on sale
  410. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  411. return false;
  412. }
  413. if( sale->amount < quantity ){
  414. // Client tried to buy a higher quantity than is available
  415. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  416. // Maybe he did not get refreshed in time -> do it now
  417. clif_sale_amount( sale, &sd->bl, SELF );
  418. return false;
  419. }
  420. }
  421. #endif
  422. switch( pc_checkadditem( sd, nameid, quantity ) ){
  423. case CHKADDITEM_EXIST:
  424. break;
  425. case CHKADDITEM_NEW:
  426. new_ += id->inventorySlotNeeded(quantity);
  427. break;
  428. case CHKADDITEM_OVERAMOUNT:
  429. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_OVER_PRODUCT_TOTAL_CNT );
  430. return false;
  431. }
  432. totalcash += cash_shop_items[tab].item[j]->price * quantity;
  433. totalweight += itemdb_weight( nameid ) * quantity;
  434. }
  435. if( ( totalweight + sd->weight ) > sd->max_weight ){
  436. clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_INVENTORY_WEIGHT );
  437. return false;
  438. }else if( pc_inventoryblank( sd ) < new_ ){
  439. clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_INVENTORY_ITEMCNT );
  440. return false;
  441. }
  442. if(pc_paycash( sd, totalcash, kafrapoints, LOG_TYPE_CASH ) <= 0){
  443. clif_cashshop_result( sd, 0, CASHSHOP_RESULT_ERROR_SHORTTAGE_CASH );
  444. return false;
  445. }
  446. for( i = 0; i < n; ++i ){
  447. t_itemid nameid = item_list[i].itemId;
  448. uint32 quantity = item_list[i].amount;
  449. #if PACKETVER_SUPPORTS_SALES
  450. uint16 tab = item_list[i].tab;
  451. #endif
  452. struct item_data *id = itemdb_search(nameid);
  453. if (!id)
  454. continue;
  455. unsigned short get_amt = quantity;
  456. if (id->flag.guid || !itemdb_isstackable2(id))
  457. get_amt = 1;
  458. #if PACKETVER_SUPPORTS_SALES
  459. struct sale_item_data* sale = nullptr;
  460. if( tab == CASHSHOP_TAB_SALE ){
  461. sale = sale_find_item( nameid, true );
  462. if( sale == nullptr ){
  463. // Client tried to buy an item from sale that was not even on sale
  464. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  465. return false;
  466. }
  467. if( sale->amount < quantity ){
  468. // Client tried to buy a higher quantity than is available
  469. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_UNKNOWN );
  470. // Maybe he did not get refreshed in time -> do it now
  471. clif_sale_amount( sale, &sd->bl, SELF );
  472. return false;
  473. }
  474. }
  475. #endif
  476. for (uint32 j = 0; j < quantity; j += get_amt) {
  477. if( !pet_create_egg( sd, nameid ) ){
  478. struct item item_tmp = { 0 };
  479. item_tmp.nameid = nameid;
  480. item_tmp.identify = 1;
  481. switch( pc_additem( sd, &item_tmp, get_amt, LOG_TYPE_CASH ) ){
  482. case ADDITEM_OVERWEIGHT:
  483. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_INVENTORY_WEIGHT );
  484. return false;
  485. case ADDITEM_OVERITEM:
  486. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_INVENTORY_ITEMCNT );
  487. return false;
  488. case ADDITEM_OVERAMOUNT:
  489. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_OVER_PRODUCT_TOTAL_CNT );
  490. return false;
  491. case ADDITEM_STACKLIMIT:
  492. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_ERROR_RUNE_OVERCOUNT );
  493. return false;
  494. }
  495. }
  496. clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_SUCCESS );
  497. #if PACKETVER_SUPPORTS_SALES
  498. if( tab == CASHSHOP_TAB_SALE ){
  499. uint32 new_amount = sale->amount - get_amt;
  500. if( new_amount == 0 ){
  501. sale_remove_item(sale->nameid);
  502. }else{
  503. if( SQL_ERROR == Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = '%d' WHERE `nameid` = '%u'", sales_table, new_amount, nameid ) ){
  504. Sql_ShowDebug(mmysql_handle);
  505. }
  506. sale->amount = new_amount;
  507. clif_sale_amount(sale, NULL, ALL_CLIENT);
  508. }
  509. }
  510. #endif
  511. }
  512. }
  513. return true;
  514. }
  515. /*
  516. * Reloads cashshop database by destroying it and reading it again.
  517. */
  518. void cashshop_reloaddb( void ){
  519. do_final_cashshop();
  520. do_init_cashshop();
  521. }
  522. /*
  523. * Destroys cashshop class.
  524. * Closes all and cleanup.
  525. */
  526. void do_final_cashshop( void ){
  527. int tab, i;
  528. for( tab = CASHSHOP_TAB_NEW; tab < CASHSHOP_TAB_MAX; tab++ ){
  529. for( i = 0; i < cash_shop_items[tab].count; i++ ){
  530. aFree( cash_shop_items[tab].item[i] );
  531. }
  532. aFree( cash_shop_items[tab].item );
  533. }
  534. memset( cash_shop_items, 0, sizeof( cash_shop_items ) );
  535. #if PACKETVER_SUPPORTS_SALES
  536. if( sale_items.count > 0 ){
  537. for( i = 0; i < sale_items.count; i++ ){
  538. struct sale_item_data* it = sale_items.item[i];
  539. if( it->timer_start != INVALID_TIMER ){
  540. delete_timer( it->timer_start, sale_start_timer );
  541. it->timer_start = INVALID_TIMER;
  542. }
  543. if( it->timer_end != INVALID_TIMER ){
  544. delete_timer( it->timer_end, sale_end_timer );
  545. it->timer_end = INVALID_TIMER;
  546. }
  547. aFree(it);
  548. }
  549. aFree(sale_items.item);
  550. sale_items.item = NULL;
  551. sale_items.count = 0;
  552. }
  553. #endif
  554. }
  555. /*
  556. * Initializes cashshop class.
  557. * return
  558. * 0 : success
  559. */
  560. void do_init_cashshop( void ){
  561. cash_shop_defined = false;
  562. cashshop_read_db();
  563. }