storage.cpp 33 KB

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