storage.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "storage.hpp"
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include <map>
  7. #include <common/cbasetypes.hpp>
  8. #include <common/nullpo.hpp>
  9. #include <common/showmsg.hpp>
  10. #include <common/utilities.hpp>
  11. #include "battle.hpp"
  12. #include "chrif.hpp"
  13. #include "clif.hpp"
  14. #include "guild.hpp"
  15. #include "intif.hpp"
  16. #include "itemdb.hpp"
  17. #include "log.hpp"
  18. #include "map.hpp" // map_session_data
  19. #include "packets.hpp"
  20. #include "pc.hpp"
  21. #include "pc_groups.hpp"
  22. using namespace rathena;
  23. std::unordered_map<uint16, std::shared_ptr<struct s_storage_table>> storage_db;
  24. ///Databases of guild_storage : int32 guild_id -> struct guild_storage
  25. std::map<int32, struct s_storage> guild_storage_db;
  26. /**
  27. * Get storage name
  28. * @param id Storage ID
  29. * @return Storage name or "Storage" if not found
  30. **/
  31. const char *storage_getName(uint8 id) {
  32. std::shared_ptr<struct s_storage_table> storage = util::umap_find( storage_db, (uint16)id );
  33. if( storage ){
  34. return storage->name;
  35. }
  36. return "Storage";
  37. }
  38. /**
  39. * Check if sotrage ID is valid
  40. * @param id Storage ID
  41. * @return True:Valid, False:Invalid
  42. **/
  43. bool storage_exists(uint8 id) {
  44. return util::umap_find( storage_db, (uint16)id ) != nullptr;
  45. }
  46. /**
  47. * Storage item comparator (for qsort)
  48. * sort by itemid and amount
  49. * @param _i1 : item a
  50. * @param _i2 : item b
  51. * @return i1<=>i2
  52. */
  53. static int32 storage_comp_item(const void *_i1, const void *_i2)
  54. {
  55. struct item *i1 = (struct item *)_i1;
  56. struct item *i2 = (struct item *)_i2;
  57. if (i1->nameid == i2->nameid)
  58. return 0;
  59. else if (!(i1->nameid) || !(i1->amount))
  60. return 1;
  61. else if (!(i2->nameid) || !(i2->amount))
  62. return -1;
  63. return i1->nameid - i2->nameid;
  64. }
  65. /**
  66. * Sort item by storage_comp_item (nameid)
  67. * used when we open up our storage or guild_storage
  68. * @param items : list of items to sort
  69. * @param size : number of item in list
  70. */
  71. void storage_sortitem(struct item* items, uint32 size)
  72. {
  73. nullpo_retv(items);
  74. if( battle_config.client_sort_storage )
  75. qsort(items, size, sizeof(struct item), storage_comp_item);
  76. }
  77. /**
  78. * Initiate storage module
  79. * Called from map.cpp::do_init()
  80. */
  81. void do_init_storage(void)
  82. {
  83. }
  84. /**
  85. * Destroy storage module
  86. * @author : [MC Cameri]
  87. * Called from map.cpp::do_final()
  88. */
  89. void do_final_storage(void)
  90. {
  91. guild_storage_db.clear();
  92. storage_db.clear();
  93. }
  94. /**
  95. * Function to be invoked upon server reconnection to char. To save all 'dirty' storages
  96. * @author [Skotlex]
  97. */
  98. void do_reconnect_storage(void){
  99. for( const auto& entry : guild_storage_db ){
  100. struct s_storage stor = entry.second;
  101. // Save closed storages.
  102. if( stor.dirty && stor.status == 0 ){
  103. storage_guild_storagesave(0, stor.id, 0);
  104. }
  105. }
  106. }
  107. /**
  108. * Player attempt tp open his storage.
  109. * @param sd : player
  110. * @return 0:success, 1:fail
  111. */
  112. int32 storage_storageopen(map_session_data *sd)
  113. {
  114. nullpo_ret(sd);
  115. if(sd->state.storage_flag)
  116. return 1; //Already open?
  117. if( !pc_can_give_items(sd) ) { // check is this GM level is allowed to put items to storage
  118. clif_displaymessage( sd->fd, msg_txt( sd, 246 ) ); // Your GM level doesn't authorize you to perform this action.
  119. return 1;
  120. }
  121. sd->state.storage_flag = 1;
  122. storage_sortitem(sd->storage.u.items_storage, ARRAYLENGTH(sd->storage.u.items_storage));
  123. clif_storagelist(sd, sd->storage.u.items_storage, ARRAYLENGTH(sd->storage.u.items_storage), storage_getName(0));
  124. clif_updatestorageamount(*sd, sd->storage.amount, sd->storage.max_amount);
  125. return 0;
  126. }
  127. /**
  128. * Check if 2 item a and b have same values
  129. * @param a : item 1
  130. * @param b : item 2
  131. * @return 1:same, 0:are different
  132. */
  133. int32 compare_item(struct item *a, struct item *b)
  134. {
  135. if( a->nameid == b->nameid &&
  136. a->identify == b->identify &&
  137. a->refine == b->refine &&
  138. a->attribute == b->attribute &&
  139. a->expire_time == b->expire_time &&
  140. a->bound == b->bound &&
  141. a->unique_id == b->unique_id
  142. )
  143. {
  144. int32 i;
  145. for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
  146. return (i == MAX_SLOTS);
  147. }
  148. return 0;
  149. }
  150. /**
  151. * Check if item can be added to storage
  152. * @param stor Storage data
  153. * @param idx Index item from inventory/cart
  154. * @param items List of items from inventory/cart
  155. * @param amount Amount of item will be added
  156. * @param max_num Max inventory/cart
  157. * @return @see enum e_storage_add
  158. **/
  159. static enum e_storage_add storage_canAddItem(struct s_storage *stor, int32 idx, struct item items[], int32 amount, int32 max_num) {
  160. if (idx < 0 || idx >= max_num)
  161. return STORAGE_ADD_INVALID;
  162. if (items[idx].nameid <= 0)
  163. return STORAGE_ADD_INVALID; // No item on that spot
  164. if (amount < 1 || amount > items[idx].amount)
  165. return STORAGE_ADD_INVALID;
  166. if (itemdb_ishatched_egg(&items[idx]))
  167. return STORAGE_ADD_INVALID;
  168. if (!stor->state.put)
  169. return STORAGE_ADD_NOACCESS;
  170. return STORAGE_ADD_OK;
  171. }
  172. /**
  173. * Check if item can be moved from storage
  174. * @param stor Storage data
  175. * @param idx Index from storage
  176. * @param amount Number of item
  177. * @return @see enum e_storage_add
  178. **/
  179. static enum e_storage_add storage_canGetItem(struct s_storage *stor, int32 idx, int32 amount) {
  180. // If last index check is sd->storage_size, if player isn't VIP anymore but there's item, player can't take it
  181. // Example the storage size when not VIP anymore is 350/300, player still can take the 301st~349th item.
  182. if( idx < 0 || idx >= ARRAYLENGTH(stor->u.items_storage) )
  183. return STORAGE_ADD_INVALID;
  184. if( stor->u.items_storage[idx].nameid <= 0 )
  185. return STORAGE_ADD_INVALID; //Nothing there
  186. if( amount < 1 || amount > stor->u.items_storage[idx].amount )
  187. return STORAGE_ADD_INVALID;
  188. if (!stor->state.get)
  189. return STORAGE_ADD_NOACCESS;
  190. return STORAGE_ADD_OK;
  191. }
  192. /**
  193. * Make a player add an item to his storage
  194. * @param sd : player
  195. * @param stor : Storage data
  196. * @param item_data : item to add
  197. * @param amount : quantity of items
  198. * @return 0:success, 1:failed, 2:failed because of room or stack checks
  199. */
  200. static int32 storage_additem(map_session_data* sd, struct s_storage *stor, struct item *it, int32 amount)
  201. {
  202. struct item_data *data;
  203. int32 i;
  204. if( it->nameid == 0 || amount <= 0 )
  205. return 1;
  206. data = itemdb_search(it->nameid);
  207. if( data->stack.storage && amount > data->stack.amount ) // item stack limitation
  208. return 2;
  209. if( !itemdb_canstore(it, pc_get_group_level(sd)) ) { // Check if item is storable. [Skotlex]
  210. clif_displaymessage (sd->fd, msg_txt(sd,264));
  211. return 1;
  212. }
  213. if( (it->bound > BOUND_ACCOUNT) && !pc_can_give_bounded_items(sd) ) {
  214. clif_displaymessage(sd->fd, msg_txt(sd,294));
  215. return 1;
  216. }
  217. if( itemdb_isstackable2(data) ) { // Stackable
  218. for( i = 0; i < stor->max_amount; i++ ) {
  219. if( compare_item(&stor->u.items_storage[i], it) ) { // existing items found, stack them
  220. if( amount > MAX_AMOUNT - stor->u.items_storage[i].amount || ( data->stack.storage && amount > data->stack.amount - stor->u.items_storage[i].amount ) )
  221. return 2;
  222. stor->u.items_storage[i].amount += amount;
  223. stor->dirty = true;
  224. clif_storageitemadded(sd,&stor->u.items_storage[i],i,amount);
  225. return 0;
  226. }
  227. }
  228. }
  229. if( stor->amount >= stor->max_amount )
  230. return 2;
  231. // find free slot
  232. ARR_FIND( 0, stor->max_amount, i, stor->u.items_storage[i].nameid == 0 );
  233. if( i >= stor->max_amount )
  234. return 2;
  235. // add item to slot
  236. memcpy(&stor->u.items_storage[i],it,sizeof(stor->u.items_storage[0]));
  237. stor->amount++;
  238. stor->u.items_storage[i].amount = amount;
  239. stor->dirty = true;
  240. clif_storageitemadded(sd,&stor->u.items_storage[i],i,amount);
  241. clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
  242. return 0;
  243. }
  244. /**
  245. * Make a player delete an item from his storage
  246. * @param sd : player
  247. * @param n : idx on storage to remove the item from
  248. * @param amount :number of item to remove
  249. * @return 0:sucess, 1:fail
  250. */
  251. int32 storage_delitem(map_session_data* sd, struct s_storage *stor, int32 index, int32 amount)
  252. {
  253. if( stor->u.items_storage[index].nameid == 0 || stor->u.items_storage[index].amount < amount )
  254. return 1;
  255. stor->u.items_storage[index].amount -= amount;
  256. stor->dirty = true;
  257. if( stor->u.items_storage[index].amount == 0 ) {
  258. memset(&stor->u.items_storage[index],0,sizeof(stor->u.items_storage[0]));
  259. stor->amount--;
  260. if( sd->state.storage_flag == 1 || sd->state.storage_flag == 3 )
  261. clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
  262. }
  263. if( sd->state.storage_flag == 1 || sd->state.storage_flag == 3 )
  264. clif_storageitemremoved( *sd, index, amount );
  265. return 0;
  266. }
  267. /**
  268. * Add an item to the storage from the inventory.
  269. * @param sd : player
  270. * @param stor : Storage data
  271. * @param index : inventory index to take the item from
  272. * @param amount : number of item to take
  273. * @return 0:fail, 1:success
  274. */
  275. void storage_storageadd(map_session_data* sd, struct s_storage *stor, int32 index, int32 amount)
  276. {
  277. enum e_storage_add result;
  278. nullpo_retv(sd);
  279. result = storage_canAddItem(stor, index, sd->inventory.u.items_inventory, amount, MAX_INVENTORY);
  280. if (result == STORAGE_ADD_INVALID)
  281. return;
  282. else if (result == STORAGE_ADD_OK) {
  283. switch( storage_additem(sd, stor, &sd->inventory.u.items_inventory[index], amount) ){
  284. case 0:
  285. pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE);
  286. return;
  287. case 1:
  288. break;
  289. case 2:
  290. result = STORAGE_ADD_NOROOM;
  291. break;
  292. }
  293. }
  294. clif_storageitemremoved( *sd, index, 0 );
  295. clif_dropitem( *sd, index, 0 );
  296. }
  297. /**
  298. * Retrieve an item from the storage into inventory
  299. * @param sd : player
  300. * @param index : storage index to take the item from
  301. * @param amount : number of item to take
  302. * @return 0:fail, 1:success
  303. */
  304. void storage_storageget(map_session_data *sd, struct s_storage *stor, int32 index, int32 amount)
  305. {
  306. unsigned char flag = 0;
  307. enum e_storage_add result;
  308. nullpo_retv(sd);
  309. result = storage_canGetItem(stor, index, amount);
  310. if (result != STORAGE_ADD_OK)
  311. return;
  312. if ((flag = pc_additem(sd,&stor->u.items_storage[index],amount,LOG_TYPE_STORAGE)) == ADDITEM_SUCCESS)
  313. storage_delitem(sd,stor,index,amount);
  314. else {
  315. clif_storageitemremoved( *sd, index, 0 );
  316. clif_additem(sd,0,0,flag);
  317. }
  318. }
  319. /**
  320. * Move an item from cart to storage.
  321. * @param sd : player
  322. * @param stor : Storage data
  323. * @param index : cart index to take the item from
  324. * @param amount : number of item to take
  325. * @return 0:fail, 1:success
  326. */
  327. void storage_storageaddfromcart(map_session_data *sd, struct s_storage *stor, int32 index, int32 amount)
  328. {
  329. enum e_storage_add result;
  330. nullpo_retv(sd);
  331. if (sd->state.prevend) {
  332. return;
  333. }
  334. result = storage_canAddItem(stor, index, sd->cart.u.items_cart, amount, MAX_CART);
  335. if (result == STORAGE_ADD_INVALID)
  336. return;
  337. else if (result == STORAGE_ADD_OK) {
  338. switch( storage_additem(sd, stor, &sd->cart.u.items_cart[index], amount) ){
  339. case 0:
  340. pc_cart_delitem(sd,index,amount,0,LOG_TYPE_STORAGE);
  341. return;
  342. case 1:
  343. break;
  344. case 2:
  345. result = STORAGE_ADD_NOROOM;
  346. break;
  347. }
  348. }
  349. clif_storageitemremoved( *sd, index, 0 );
  350. clif_dropitem( *sd, index, 0 );
  351. }
  352. /**
  353. * Get from Storage to the Cart inventory
  354. * @param sd : player
  355. * @param stor : Storage data
  356. * @param index : storage index to take the item from
  357. * @param amount : number of item to take
  358. * @return 0:fail, 1:success
  359. */
  360. void storage_storagegettocart(map_session_data* sd, struct s_storage *stor, int32 index, int32 amount)
  361. {
  362. unsigned char flag = 0;
  363. enum e_storage_add result;
  364. nullpo_retv(sd);
  365. if (sd->state.prevend) {
  366. return;
  367. }
  368. result = storage_canGetItem(stor, index, amount);
  369. if (result != STORAGE_ADD_OK)
  370. return;
  371. if ((flag = pc_cart_additem(sd,&stor->u.items_storage[index],amount,LOG_TYPE_STORAGE)) == 0)
  372. storage_delitem(sd,stor,index,amount);
  373. else {
  374. clif_storageitemremoved( *sd, index, 0 );
  375. if (flag == ADDITEM_INVALID)
  376. clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_WEIGHT );
  377. else
  378. clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_COUNT );
  379. }
  380. }
  381. /**
  382. * Request to save storage
  383. * @param sd: Player who has the storage
  384. */
  385. void storage_storagesave(map_session_data *sd)
  386. {
  387. nullpo_retv(sd);
  388. intif_storage_save(sd, &sd->storage);
  389. }
  390. /**
  391. * Make player close his storage
  392. * @param sd: Player who has the storage
  393. * @author [massdriller] / modified by [Valaris]
  394. */
  395. void storage_storageclose(map_session_data *sd)
  396. {
  397. nullpo_retv(sd);
  398. if (sd->storage.dirty) {
  399. if (save_settings&CHARSAVE_STORAGE)
  400. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  401. else
  402. storage_storagesave(sd);
  403. }
  404. if( sd->state.storage_flag == 1 ){
  405. sd->state.storage_flag = 0;
  406. clif_storageclose( *sd );
  407. }
  408. }
  409. /**
  410. * Force closing the storage for player without displaying result
  411. * (example when quitting the game)
  412. * @param sd : player to close storage
  413. * @param flag :
  414. * 1: Character is quitting
  415. * 2(x): Character is changing map-servers
  416. */
  417. void storage_storage_quit(map_session_data* sd, int32 flag)
  418. {
  419. nullpo_retv(sd);
  420. if (save_settings&CHARSAVE_STORAGE)
  421. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  422. else
  423. storage_storagesave(sd);
  424. }
  425. /**
  426. * Retrieve the guild_storage of a guild
  427. * will create a new storage if none found for the guild
  428. * @param guild_id : id of the guild
  429. * @return s_storage
  430. */
  431. struct s_storage *guild2storage(int32 guild_id)
  432. {
  433. struct s_storage *gs;
  434. if (guild_search(guild_id) == nullptr)
  435. return nullptr;
  436. gs = guild2storage2(guild_id);
  437. if( gs == nullptr ){
  438. gs = &guild_storage_db[guild_id];
  439. gs->id = guild_id;
  440. gs->type = TABLE_GUILD_STORAGE;
  441. }
  442. return gs;
  443. }
  444. /**
  445. * See if the guild_storage exist in db and fetch it if it's the case
  446. * @author : [Skotlex]
  447. * @param guild_id : guild_id to search the storage
  448. * @return s_storage or nullptr
  449. */
  450. struct s_storage *guild2storage2(int32 guild_id){
  451. return util::map_find( guild_storage_db, guild_id );
  452. }
  453. /**
  454. * Delete a guild_storage and remove it from db
  455. * @param guild_id : guild to remove the storage from
  456. */
  457. void storage_guild_delete(int32 guild_id)
  458. {
  459. guild_storage_db.erase(guild_id);
  460. }
  461. /**
  462. * Attempt to open guild storage for player
  463. * @param sd : player
  464. * @return 0 : success, 1 : fail, 2 : no guild found
  465. */
  466. char storage_guild_storageopen(map_session_data* sd)
  467. {
  468. struct s_storage *gstor;
  469. nullpo_ret(sd);
  470. if(sd->status.guild_id <= 0)
  471. return GSTORAGE_NO_GUILD;
  472. #ifdef OFFICIAL_GUILD_STORAGE
  473. if (!guild_checkskill(sd->guild->guild, GD_GUILD_STORAGE))
  474. return GSTORAGE_NO_STORAGE; // Can't open storage if the guild has not learned the skill
  475. #endif
  476. if (sd->state.storage_flag == 2)
  477. return GSTORAGE_ALREADY_OPEN; // Guild storage already open.
  478. else if (sd->state.storage_flag)
  479. return GSTORAGE_STORAGE_ALREADY_OPEN; // Can't open both storages at a time.
  480. #if PACKETVER >= 20140205
  481. int32 pos;
  482. if ((pos = guild_getposition(*sd)) < 0 || !(sd->guild->guild.position[pos].mode&GUILD_PERM_STORAGE))
  483. return GSTORAGE_NO_PERMISSION; // Guild member doesn't have permission
  484. #endif
  485. if( !pc_can_give_items(sd) ) { //check is this GM level can open guild storage and store items [Lupus]
  486. clif_displaymessage( sd->fd, msg_txt( sd, 246 ) ); // Your GM level doesn't authorize you to perform this action.
  487. return GSTORAGE_ALREADY_OPEN;
  488. }
  489. if((gstor = guild2storage2(sd->status.guild_id)) == nullptr
  490. #ifdef OFFICIAL_GUILD_STORAGE
  491. || (gstor->max_amount != guild_checkskill(sd->guild->guild, GD_GUILD_STORAGE) * 100)
  492. #endif
  493. ) {
  494. intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
  495. return GSTORAGE_OPEN;
  496. }
  497. if(gstor->status)
  498. return GSTORAGE_ALREADY_OPEN;
  499. if( gstor->lock )
  500. return GSTORAGE_ALREADY_OPEN;
  501. gstor->status = true;
  502. sd->state.storage_flag = 2;
  503. storage_sortitem(gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild));
  504. clif_storagelist(sd, gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild), "Guild Storage");
  505. clif_updatestorageamount(*sd, gstor->amount, gstor->max_amount);
  506. return GSTORAGE_OPEN;
  507. }
  508. void storage_guild_log( map_session_data* sd, struct item* item, int16 amount ){
  509. int32 i;
  510. SqlStmt stmt{ *mmysql_handle };
  511. StringBuf buf;
  512. StringBuf_Init(&buf);
  513. StringBuf_Printf(&buf, "INSERT INTO `%s` (`time`, `guild_id`, `char_id`, `name`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `unique_id`, `bound`, `enchantgrade`", guild_storage_log_table);
  514. for (i = 0; i < MAX_SLOTS; ++i)
  515. StringBuf_Printf(&buf, ", `card%d`", i);
  516. for (i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
  517. StringBuf_Printf(&buf, ", `option_id%d`", i);
  518. StringBuf_Printf(&buf, ", `option_val%d`", i);
  519. StringBuf_Printf(&buf, ", `option_parm%d`", i);
  520. }
  521. StringBuf_Printf(&buf, ") VALUES(NOW(),'%u','%u', '%s', '%u', '%d','%d','%d','%d','%" PRIu64 "','%d','%d'",
  522. sd->status.guild_id, sd->status.char_id, sd->status.name, item->nameid, amount, item->identify, item->refine,item->attribute, item->unique_id, item->bound, item->enchantgrade);
  523. for (i = 0; i < MAX_SLOTS; i++)
  524. StringBuf_Printf(&buf, ",'%u'", item->card[i]);
  525. for (i = 0; i < MAX_ITEM_RDM_OPT; i++)
  526. StringBuf_Printf(&buf, ",'%d','%d','%d'", item->option[i].id, item->option[i].value, item->option[i].param);
  527. StringBuf_Printf(&buf, ")");
  528. if (SQL_SUCCESS != stmt.PrepareStr(StringBuf_Value(&buf)) || SQL_SUCCESS != stmt.Execute())
  529. SqlStmt_ShowDebug(stmt);
  530. StringBuf_Destroy(&buf);
  531. }
  532. enum e_guild_storage_log storage_guild_log_read_sub( map_session_data* sd, std::vector<struct guild_log_entry>& log, uint32 max ){
  533. StringBuf buf;
  534. int32 j;
  535. StringBuf_Init(&buf);
  536. StringBuf_AppendStr(&buf, "SELECT `id`, `time`, `name`, `amount`");
  537. StringBuf_AppendStr(&buf, " , `nameid`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`, `enchantgrade`");
  538. for (j = 0; j < MAX_SLOTS; ++j)
  539. StringBuf_Printf(&buf, ", `card%d`", j);
  540. for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
  541. StringBuf_Printf(&buf, ", `option_id%d`", j);
  542. StringBuf_Printf(&buf, ", `option_val%d`", j);
  543. StringBuf_Printf(&buf, ", `option_parm%d`", j);
  544. }
  545. StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%u'", guild_storage_log_table, sd->status.guild_id );
  546. StringBuf_Printf(&buf, " ORDER BY `time` DESC LIMIT %u", max);
  547. SqlStmt stmt{ *mmysql_handle };
  548. if( SQL_ERROR == stmt.PrepareStr(StringBuf_Value(&buf)) ||
  549. SQL_ERROR == stmt.Execute() )
  550. {
  551. SqlStmt_ShowDebug(stmt);
  552. StringBuf_Destroy(&buf);
  553. return GUILDSTORAGE_LOG_FAILED;
  554. }
  555. struct guild_log_entry entry;
  556. // General data
  557. stmt.BindColumn(0, SQLDT_UINT32, &entry.id, 0, nullptr, nullptr);
  558. stmt.BindColumn(1, SQLDT_STRING, &entry.time, sizeof(entry.time), nullptr, nullptr);
  559. stmt.BindColumn(2, SQLDT_STRING, &entry.name, sizeof(entry.name), nullptr, nullptr);
  560. stmt.BindColumn(3, SQLDT_INT16, &entry.amount, 0, nullptr, nullptr);
  561. // Item data
  562. stmt.BindColumn(4, SQLDT_UINT32, &entry.item.nameid, 0, nullptr, nullptr);
  563. stmt.BindColumn(5, SQLDT_CHAR, &entry.item.identify, 0, nullptr, nullptr);
  564. stmt.BindColumn(6, SQLDT_CHAR, &entry.item.refine, 0, nullptr, nullptr);
  565. stmt.BindColumn(7, SQLDT_CHAR, &entry.item.attribute, 0, nullptr, nullptr);
  566. stmt.BindColumn(8, SQLDT_UINT32, &entry.item.expire_time, 0, nullptr, nullptr);
  567. stmt.BindColumn(9, SQLDT_UINT32, &entry.item.bound, 0, nullptr, nullptr);
  568. stmt.BindColumn(10, SQLDT_UINT64, &entry.item.unique_id, 0, nullptr, nullptr);
  569. stmt.BindColumn(11, SQLDT_INT8, &entry.item.enchantgrade,0, nullptr, nullptr);
  570. for( j = 0; j < MAX_SLOTS; ++j )
  571. stmt.BindColumn(12+j, SQLDT_UINT32, &entry.item.card[j], 0, nullptr, nullptr);
  572. for( j = 0; j < MAX_ITEM_RDM_OPT; ++j ) {
  573. stmt.BindColumn(12+MAX_SLOTS+j*3, SQLDT_INT16, &entry.item.option[j].id, 0, nullptr, nullptr);
  574. stmt.BindColumn(12+MAX_SLOTS+j*3+1, SQLDT_INT16, &entry.item.option[j].value, 0, nullptr, nullptr);
  575. stmt.BindColumn(12+MAX_SLOTS+j*3+2, SQLDT_CHAR, &entry.item.option[j].param, 0, nullptr, nullptr);
  576. }
  577. log.reserve(max);
  578. while( SQL_SUCCESS == stmt.NextRow() ){
  579. log.push_back( entry );
  580. }
  581. Sql_FreeResult(mmysql_handle);
  582. StringBuf_Destroy(&buf);
  583. if( log.empty() ){
  584. return GUILDSTORAGE_LOG_EMPTY;
  585. }
  586. return GUILDSTORAGE_LOG_FINAL_SUCCESS;
  587. }
  588. enum e_guild_storage_log storage_guild_log_read( map_session_data* sd ){
  589. std::vector<struct guild_log_entry> log;
  590. enum e_guild_storage_log ret = storage_guild_log_read_sub( sd, log, MAX_GUILD_STORAGE_LOG_PACKET );
  591. clif_guild_storage_log( *sd, log, ret );
  592. return ret;
  593. }
  594. /**
  595. * Attempt to add an item in guild storage, then refresh it
  596. * @param sd : player attempting to open the guild_storage
  597. * @param stor : guild_storage
  598. * @param item : item to add
  599. * @param amount : number of item to add
  600. * @return True : success, False : fail
  601. */
  602. bool storage_guild_additem(map_session_data* sd, struct s_storage* stor, struct item* item_data, int32 amount)
  603. {
  604. struct item_data *id;
  605. int32 i;
  606. nullpo_ret(sd);
  607. nullpo_ret(stor);
  608. nullpo_ret(item_data);
  609. if(item_data->nameid == 0 || amount <= 0)
  610. return false;
  611. id = itemdb_search(item_data->nameid);
  612. if( id->stack.guild_storage && amount > id->stack.amount ) // item stack limitation
  613. return false;
  614. if (!itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time) { // Check if item is storable. [Skotlex]
  615. clif_displaymessage (sd->fd, msg_txt(sd,264));
  616. return false;
  617. }
  618. if ((item_data->bound == BOUND_ACCOUNT || item_data->bound > BOUND_GUILD) && !pc_can_give_bounded_items(sd)) {
  619. clif_displaymessage(sd->fd, msg_txt(sd,294));
  620. return false;
  621. }
  622. if(itemdb_isstackable2(id)) { //Stackable
  623. for(i = 0; i < stor->max_amount; i++) {
  624. if(compare_item(&stor->u.items_guild[i], item_data)) {
  625. if( amount > MAX_AMOUNT - stor->u.items_guild[i].amount || ( id->stack.guild_storage && amount > id->stack.amount - stor->u.items_guild[i].amount ) )
  626. return false;
  627. stor->u.items_guild[i].amount += amount;
  628. clif_storageitemadded(sd,&stor->u.items_guild[i],i,amount);
  629. stor->dirty = true;
  630. storage_guild_log( sd, &stor->u.items_guild[i], amount );
  631. return true;
  632. }
  633. }
  634. }
  635. //Add item
  636. for(i = 0; i < stor->max_amount && stor->u.items_guild[i].nameid; i++);
  637. if(i >= stor->max_amount)
  638. return false;
  639. memcpy(&stor->u.items_guild[i],item_data,sizeof(stor->u.items_guild[0]));
  640. stor->u.items_guild[i].amount = amount;
  641. stor->amount++;
  642. clif_storageitemadded(sd,&stor->u.items_guild[i],i,amount);
  643. clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
  644. stor->dirty = true;
  645. storage_guild_log( sd, &stor->u.items_guild[i], amount );
  646. return true;
  647. }
  648. /**
  649. * Attempt to add an item in guild storage, then refresh i
  650. * @param stor : guild_storage
  651. * @param item : item to add
  652. * @param amount : number of item to add
  653. * @return True : success, False : fail
  654. */
  655. bool storage_guild_additem2(struct s_storage* stor, struct item* item, int32 amount) {
  656. int32 i;
  657. nullpo_ret(stor);
  658. nullpo_ret(item);
  659. if (item->nameid == 0 || amount <= 0)
  660. return false;
  661. std::shared_ptr<item_data> id = item_db.find(item->nameid);
  662. if (id == nullptr || item->expire_time)
  663. return false;
  664. if (itemdb_isstackable2(id.get())) { // Stackable
  665. for (i = 0; i < stor->max_amount; i++) {
  666. if (compare_item(&stor->u.items_guild[i], item)) {
  667. // Set the amount, make it fit with max amount
  668. amount = min(amount, ((id->stack.guild_storage) ? id->stack.amount : MAX_AMOUNT) - stor->u.items_guild[i].amount);
  669. if (amount != item->amount)
  670. ShowWarning("storage_guild_additem2: Stack limit reached! Altered amount of item \"" CL_WHITE "%s" CL_RESET "\" (%u). '" CL_WHITE "%d" CL_RESET "' -> '" CL_WHITE"%d" CL_RESET "'.\n", id->name.c_str(), id->nameid, item->amount, amount);
  671. stor->u.items_guild[i].amount += amount;
  672. stor->dirty = true;
  673. return true;
  674. }
  675. }
  676. }
  677. // Add the item
  678. for (i = 0; i < stor->max_amount && stor->u.items_guild[i].nameid; i++);
  679. if (i >= stor->max_amount)
  680. return false;
  681. memcpy(&stor->u.items_guild[i], item, sizeof(stor->u.items_guild[0]));
  682. stor->u.items_guild[i].amount = amount;
  683. stor->amount++;
  684. stor->dirty = true;
  685. return true;
  686. }
  687. /**
  688. * Attempt to delete an item in guild storage, then refresh it
  689. * @param sd : player
  690. * @param stor : guild_storage
  691. * @param n : index of item in guild storage
  692. * @param amount : number of item to delete
  693. * @return True : success, False : fail
  694. */
  695. bool storage_guild_delitem(map_session_data* sd, struct s_storage* stor, int32 n, int32 amount)
  696. {
  697. nullpo_retr(1, sd);
  698. nullpo_retr(1, stor);
  699. if(!stor->u.items_guild[n].nameid || stor->u.items_guild[n].amount < amount)
  700. return false;
  701. // Log before removing it
  702. storage_guild_log( sd, &stor->u.items_guild[n], -amount );
  703. stor->u.items_guild[n].amount -= amount;
  704. if(!stor->u.items_guild[n].amount) {
  705. memset(&stor->u.items_guild[n],0,sizeof(stor->u.items_guild[0]));
  706. stor->amount--;
  707. clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
  708. }
  709. clif_storageitemremoved( *sd, n, amount );
  710. stor->dirty = true;
  711. return true;
  712. }
  713. /**
  714. * Attempt to add an item in guild storage from inventory, then refresh it
  715. * @param sd : player
  716. * @param amount : number of item to delete
  717. */
  718. void storage_guild_storageadd(map_session_data* sd, int32 index, int32 amount)
  719. {
  720. struct s_storage *stor;
  721. nullpo_retv(sd);
  722. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  723. if( !stor->status || stor->amount > stor->max_amount )
  724. return;
  725. if( index < 0 || index >= MAX_INVENTORY )
  726. return;
  727. if( sd->inventory.u.items_inventory[index].nameid == 0 )
  728. return;
  729. if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount )
  730. return;
  731. if (itemdb_ishatched_egg(&sd->inventory.u.items_inventory[index]))
  732. return;
  733. if( stor->lock ) {
  734. storage_guild_storageclose(sd);
  735. return;
  736. }
  737. if(storage_guild_additem(sd,stor,&sd->inventory.u.items_inventory[index],amount))
  738. pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE);
  739. else {
  740. clif_storageitemremoved( *sd, index, 0 );
  741. clif_dropitem( *sd, index, 0 );
  742. }
  743. }
  744. /**
  745. * Attempt to retrieve an item from guild storage to inventory, then refresh it
  746. * @param sd : player
  747. * @param index : index of item in storage
  748. * @param amount : number of item to get
  749. * @return 1:success, 0:fail
  750. */
  751. void storage_guild_storageget(map_session_data* sd, int32 index, int32 amount)
  752. {
  753. struct s_storage *stor;
  754. unsigned char flag = 0;
  755. nullpo_retv(sd);
  756. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  757. if(!stor->status)
  758. return;
  759. if(index < 0 || index >= stor->max_amount)
  760. return;
  761. if(stor->u.items_guild[index].nameid == 0)
  762. return;
  763. if(amount < 1 || amount > stor->u.items_guild[index].amount)
  764. return;
  765. if( stor->lock ) {
  766. storage_guild_storageclose(sd);
  767. return;
  768. }
  769. if((flag = pc_additem(sd,&stor->u.items_guild[index],amount,LOG_TYPE_GSTORAGE)) == 0)
  770. storage_guild_delitem(sd,stor,index,amount);
  771. else { // inform fail
  772. clif_storageitemremoved( *sd, index, 0 );
  773. clif_additem(sd,0,0,flag);
  774. }
  775. }
  776. /**
  777. * Attempt to add an item in guild storage from cart, then refresh it
  778. * @param sd : player
  779. * @param index : index of item in cart
  780. * @param amount : number of item to transfer
  781. */
  782. void storage_guild_storageaddfromcart(map_session_data* sd, int32 index, int32 amount)
  783. {
  784. struct s_storage *stor;
  785. nullpo_retv(sd);
  786. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  787. if( !stor->status || stor->amount > stor->max_amount )
  788. return;
  789. if( index < 0 || index >= MAX_CART )
  790. return;
  791. if( sd->cart.u.items_cart[index].nameid == 0 )
  792. return;
  793. if( amount < 1 || amount > sd->cart.u.items_cart[index].amount )
  794. return;
  795. if(storage_guild_additem(sd,stor,&sd->cart.u.items_cart[index],amount))
  796. pc_cart_delitem(sd,index,amount,0,LOG_TYPE_GSTORAGE);
  797. else {
  798. clif_storageitemremoved( *sd, index, 0 );
  799. clif_dropitem( *sd, index, 0 );
  800. }
  801. }
  802. /**
  803. * Attempt to retrieve an item from guild storage to cart, then refresh it
  804. * @param sd : player
  805. * @param index : index of item in storage
  806. * @param amount : number of item to transfer
  807. * @return 1:fail, 0:success
  808. */
  809. void storage_guild_storagegettocart(map_session_data* sd, int32 index, int32 amount)
  810. {
  811. short flag;
  812. struct s_storage *stor;
  813. nullpo_retv(sd);
  814. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  815. if(!stor->status)
  816. return;
  817. if(index < 0 || index >= stor->max_amount)
  818. return;
  819. if(stor->u.items_guild[index].nameid == 0)
  820. return;
  821. if(amount < 1 || amount > stor->u.items_guild[index].amount)
  822. return;
  823. if((flag = pc_cart_additem(sd,&stor->u.items_guild[index],amount,LOG_TYPE_GSTORAGE)) == 0)
  824. storage_guild_delitem(sd,stor,index,amount);
  825. else {
  826. clif_storageitemremoved( *sd, index, 0 );
  827. if (flag == ADDITEM_INVALID)
  828. clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_WEIGHT );
  829. else
  830. clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_COUNT );
  831. }
  832. }
  833. /**
  834. * Request to save guild storage
  835. * @param account_id : account requesting the save
  836. * @param guild_id : guild to take the guild_storage
  837. * @param flag : 1=char quitting, close the storage
  838. * @return False : fail (no storage), True : success (requested)
  839. */
  840. bool storage_guild_storagesave(uint32 account_id, int32 guild_id, int32 flag)
  841. {
  842. struct s_storage *stor = guild2storage2(guild_id);
  843. if (stor) {
  844. if (flag&CSAVE_QUIT) //Char quitting, close it.
  845. stor->status = false;
  846. if (stor->dirty)
  847. intif_send_guild_storage(account_id,stor);
  848. return true;
  849. }
  850. return false;
  851. }
  852. /**
  853. * ACK save of guild storage
  854. * @param guild_id : guild to use the storage
  855. */
  856. void storage_guild_storagesaved(int32 guild_id)
  857. {
  858. struct s_storage *stor;
  859. if ((stor = guild2storage2(guild_id)) != nullptr) {
  860. if (stor->dirty && !stor->status) // Storage has been correctly saved.
  861. stor->dirty = false;
  862. }
  863. }
  864. /**
  865. * Close storage for player then save it
  866. * @param sd : player
  867. */
  868. void storage_guild_storageclose(map_session_data* sd)
  869. {
  870. struct s_storage *stor;
  871. nullpo_retv(sd);
  872. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  873. clif_storageclose( *sd );
  874. if (stor->status) {
  875. if (save_settings&CHARSAVE_STORAGE)
  876. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART); //This one also saves the storage. [Skotlex]
  877. else
  878. storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0);
  879. stor->status = false;
  880. }
  881. sd->state.storage_flag = 0;
  882. }
  883. /**
  884. * Close storage for player then save it
  885. * @param sd
  886. * @param flag
  887. */
  888. void storage_guild_storage_quit(map_session_data* sd, int32 flag)
  889. {
  890. struct s_storage *stor;
  891. nullpo_retv(sd);
  892. nullpo_retv(stor = guild2storage2(sd->status.guild_id));
  893. if (flag) { //Only during a guild break flag is 1 (don't save storage)
  894. clif_storageclose( *sd );
  895. if (save_settings&CHARSAVE_STORAGE)
  896. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  897. sd->state.storage_flag = 0;
  898. stor->status = false;
  899. return;
  900. }
  901. if (stor->status) {
  902. if (save_settings&CHARSAVE_STORAGE)
  903. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  904. else
  905. storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1);
  906. }
  907. sd->state.storage_flag = 0;
  908. stor->status = false;
  909. }
  910. /**
  911. * Open premium storage
  912. * @param sd Player
  913. **/
  914. void storage_premiumStorage_open(map_session_data *sd) {
  915. nullpo_retv(sd);
  916. sd->state.storage_flag = 3;
  917. storage_sortitem(sd->premiumStorage.u.items_storage, ARRAYLENGTH(sd->premiumStorage.u.items_storage));
  918. clif_storagelist(sd, sd->premiumStorage.u.items_storage, ARRAYLENGTH(sd->premiumStorage.u.items_storage), storage_getName(sd->premiumStorage.stor_id));
  919. clif_updatestorageamount(*sd, sd->premiumStorage.amount, sd->premiumStorage.max_amount);
  920. }
  921. /**
  922. * Request to open premium storage
  923. * @param sd Player who request
  924. * @param num Storage number
  925. * @param mode Storage mode @see enum e_storage_mode
  926. * @return 1:Success to request, 0:Failed
  927. * @author [Cydh]
  928. **/
  929. bool storage_premiumStorage_load(map_session_data *sd, uint8 num, uint8 mode) {
  930. nullpo_ret(sd);
  931. if (sd->state.storage_flag)
  932. return 0;
  933. if (sd->state.vending || sd->state.buyingstore || sd->state.prevend || sd->state.autotrade)
  934. return 0;
  935. if (sd->state.banking || sd->state.callshop)
  936. return 0;
  937. if (!pc_can_give_items(sd)) { // check is this GM level is allowed to put items to storage
  938. clif_displaymessage( sd->fd, msg_txt( sd, 246 ) ); // Your GM level doesn't authorize you to perform this action.
  939. return 0;
  940. }
  941. if (sd->premiumStorage.stor_id != num)
  942. return intif_storage_request(sd, TABLE_STORAGE, num, mode);
  943. else {
  944. sd->premiumStorage.state.put = (mode&STOR_MODE_PUT) ? 1 : 0;
  945. sd->premiumStorage.state.get = (mode&STOR_MODE_GET) ? 1 : 0;
  946. storage_premiumStorage_open(sd);
  947. }
  948. return 1;
  949. }
  950. /**
  951. * Request to save premium storage
  952. * @param sd Player who has the storage
  953. * @author [Cydh]
  954. **/
  955. void storage_premiumStorage_save(map_session_data *sd) {
  956. nullpo_retv(sd);
  957. intif_storage_save(sd, &sd->premiumStorage);
  958. }
  959. /**
  960. * Request to close premium storage
  961. * @param sd Player who has the storage
  962. * @author [Cydh]
  963. **/
  964. void storage_premiumStorage_close(map_session_data *sd) {
  965. nullpo_retv(sd);
  966. if (sd->premiumStorage.dirty) {
  967. if (save_settings&CHARSAVE_STORAGE)
  968. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  969. else
  970. storage_premiumStorage_save(sd);
  971. }
  972. if( sd->state.storage_flag == 3 ){
  973. sd->state.storage_flag = 0;
  974. clif_storageclose( *sd );
  975. }
  976. }
  977. /**
  978. * Force save the premium storage
  979. * @param sd Player who has the storage
  980. * @author [Cydh]
  981. **/
  982. void storage_premiumStorage_quit(map_session_data *sd) {
  983. nullpo_retv(sd);
  984. if (save_settings&CHARSAVE_STORAGE)
  985. chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
  986. else
  987. storage_premiumStorage_save(sd);
  988. }