storage.cpp 33 KB

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