cashshop.cpp 18 KB

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