123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209 |
- // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
- // For more information, see LICENCE in the main folder
- #include "storage.hpp"
- #include <cstdlib>
- #include <cstring>
- #include <map>
- #include <common/cbasetypes.hpp>
- #include <common/nullpo.hpp>
- #include <common/showmsg.hpp>
- #include <common/utilities.hpp>
- #include "battle.hpp"
- #include "chrif.hpp"
- #include "clif.hpp"
- #include "guild.hpp"
- #include "intif.hpp"
- #include "itemdb.hpp"
- #include "log.hpp"
- #include "map.hpp" // map_session_data
- #include "packets.hpp"
- #include "pc.hpp"
- #include "pc_groups.hpp"
- using namespace rathena;
- std::unordered_map<uint16, std::shared_ptr<struct s_storage_table>> storage_db;
- ///Databases of guild_storage : int guild_id -> struct guild_storage
- std::map<int, struct s_storage> guild_storage_db;
- /**
- * Get storage name
- * @param id Storage ID
- * @return Storage name or "Storage" if not found
- **/
- const char *storage_getName(uint8 id) {
- std::shared_ptr<struct s_storage_table> storage = util::umap_find( storage_db, (uint16)id );
- if( storage ){
- return storage->name;
- }
- return "Storage";
- }
- /**
- * Check if sotrage ID is valid
- * @param id Storage ID
- * @return True:Valid, False:Invalid
- **/
- bool storage_exists(uint8 id) {
- return util::umap_find( storage_db, (uint16)id ) != nullptr;
- }
- /**
- * Storage item comparator (for qsort)
- * sort by itemid and amount
- * @param _i1 : item a
- * @param _i2 : item b
- * @return i1<=>i2
- */
- static int storage_comp_item(const void *_i1, const void *_i2)
- {
- struct item *i1 = (struct item *)_i1;
- struct item *i2 = (struct item *)_i2;
- if (i1->nameid == i2->nameid)
- return 0;
- else if (!(i1->nameid) || !(i1->amount))
- return 1;
- else if (!(i2->nameid) || !(i2->amount))
- return -1;
- return i1->nameid - i2->nameid;
- }
- /**
- * Sort item by storage_comp_item (nameid)
- * used when we open up our storage or guild_storage
- * @param items : list of items to sort
- * @param size : number of item in list
- */
- void storage_sortitem(struct item* items, unsigned int size)
- {
- nullpo_retv(items);
- if( battle_config.client_sort_storage )
- qsort(items, size, sizeof(struct item), storage_comp_item);
- }
- /**
- * Initiate storage module
- * Called from map.cpp::do_init()
- */
- void do_init_storage(void)
- {
- }
- /**
- * Destroy storage module
- * @author : [MC Cameri]
- * Called from map.cpp::do_final()
- */
- void do_final_storage(void)
- {
- guild_storage_db.clear();
- storage_db.clear();
- }
- /**
- * Function to be invoked upon server reconnection to char. To save all 'dirty' storages
- * @author [Skotlex]
- */
- void do_reconnect_storage(void){
- for( const auto& entry : guild_storage_db ){
- struct s_storage stor = entry.second;
- // Save closed storages.
- if( stor.dirty && stor.status == 0 ){
- storage_guild_storagesave(0, stor.id, 0);
- }
- }
- }
- /**
- * Player attempt tp open his storage.
- * @param sd : player
- * @return 0:success, 1:fail
- */
- int storage_storageopen(map_session_data *sd)
- {
- nullpo_ret(sd);
- if(sd->state.storage_flag)
- return 1; //Already open?
- if( !pc_can_give_items(sd) ) { // check is this GM level is allowed to put items to storage
- clif_displaymessage(sd->fd, msg_txt(sd,246));
- return 1;
- }
- sd->state.storage_flag = 1;
- storage_sortitem(sd->storage.u.items_storage, ARRAYLENGTH(sd->storage.u.items_storage));
- clif_storagelist(sd, sd->storage.u.items_storage, ARRAYLENGTH(sd->storage.u.items_storage), storage_getName(0));
- clif_updatestorageamount(*sd, sd->storage.amount, sd->storage.max_amount);
- return 0;
- }
- /**
- * Check if 2 item a and b have same values
- * @param a : item 1
- * @param b : item 2
- * @return 1:same, 0:are different
- */
- int compare_item(struct item *a, struct item *b)
- {
- if( a->nameid == b->nameid &&
- a->identify == b->identify &&
- a->refine == b->refine &&
- a->attribute == b->attribute &&
- a->expire_time == b->expire_time &&
- a->bound == b->bound &&
- a->unique_id == b->unique_id
- )
- {
- int i;
- for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
- return (i == MAX_SLOTS);
- }
- return 0;
- }
- /**
- * Check if item can be added to storage
- * @param stor Storage data
- * @param idx Index item from inventory/cart
- * @param items List of items from inventory/cart
- * @param amount Amount of item will be added
- * @param max_num Max inventory/cart
- * @return @see enum e_storage_add
- **/
- static enum e_storage_add storage_canAddItem(struct s_storage *stor, int idx, struct item items[], int amount, int max_num) {
- if (idx < 0 || idx >= max_num)
- return STORAGE_ADD_INVALID;
- if (items[idx].nameid <= 0)
- return STORAGE_ADD_INVALID; // No item on that spot
- if (amount < 1 || amount > items[idx].amount)
- return STORAGE_ADD_INVALID;
- if (itemdb_ishatched_egg(&items[idx]))
- return STORAGE_ADD_INVALID;
- if (!stor->state.put)
- return STORAGE_ADD_NOACCESS;
- return STORAGE_ADD_OK;
- }
- /**
- * Check if item can be moved from storage
- * @param stor Storage data
- * @param idx Index from storage
- * @param amount Number of item
- * @return @see enum e_storage_add
- **/
- static enum e_storage_add storage_canGetItem(struct s_storage *stor, int idx, int amount) {
- // If last index check is sd->storage_size, if player isn't VIP anymore but there's item, player can't take it
- // Example the storage size when not VIP anymore is 350/300, player still can take the 301st~349th item.
- if( idx < 0 || idx >= ARRAYLENGTH(stor->u.items_storage) )
- return STORAGE_ADD_INVALID;
- if( stor->u.items_storage[idx].nameid <= 0 )
- return STORAGE_ADD_INVALID; //Nothing there
- if( amount < 1 || amount > stor->u.items_storage[idx].amount )
- return STORAGE_ADD_INVALID;
- if (!stor->state.get)
- return STORAGE_ADD_NOACCESS;
- return STORAGE_ADD_OK;
- }
- /**
- * Make a player add an item to his storage
- * @param sd : player
- * @param stor : Storage data
- * @param item_data : item to add
- * @param amount : quantity of items
- * @return 0:success, 1:failed, 2:failed because of room or stack checks
- */
- static int storage_additem(map_session_data* sd, struct s_storage *stor, struct item *it, int amount)
- {
- struct item_data *data;
- int i;
- if( it->nameid == 0 || amount <= 0 )
- return 1;
- data = itemdb_search(it->nameid);
- if( data->stack.storage && amount > data->stack.amount ) // item stack limitation
- return 2;
- if( !itemdb_canstore(it, pc_get_group_level(sd)) ) { // Check if item is storable. [Skotlex]
- clif_displaymessage (sd->fd, msg_txt(sd,264));
- return 1;
- }
- if( (it->bound > BOUND_ACCOUNT) && !pc_can_give_bounded_items(sd) ) {
- clif_displaymessage(sd->fd, msg_txt(sd,294));
- return 1;
- }
- if( itemdb_isstackable2(data) ) { // Stackable
- for( i = 0; i < stor->max_amount; i++ ) {
- if( compare_item(&stor->u.items_storage[i], it) ) { // existing items found, stack them
- if( amount > MAX_AMOUNT - stor->u.items_storage[i].amount || ( data->stack.storage && amount > data->stack.amount - stor->u.items_storage[i].amount ) )
- return 2;
- stor->u.items_storage[i].amount += amount;
- stor->dirty = true;
- clif_storageitemadded(sd,&stor->u.items_storage[i],i,amount);
- return 0;
- }
- }
- }
- if( stor->amount >= stor->max_amount )
- return 2;
- // find free slot
- ARR_FIND( 0, stor->max_amount, i, stor->u.items_storage[i].nameid == 0 );
- if( i >= stor->max_amount )
- return 2;
- // add item to slot
- memcpy(&stor->u.items_storage[i],it,sizeof(stor->u.items_storage[0]));
- stor->amount++;
- stor->u.items_storage[i].amount = amount;
- stor->dirty = true;
- clif_storageitemadded(sd,&stor->u.items_storage[i],i,amount);
- clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
- return 0;
- }
- /**
- * Make a player delete an item from his storage
- * @param sd : player
- * @param n : idx on storage to remove the item from
- * @param amount :number of item to remove
- * @return 0:sucess, 1:fail
- */
- int storage_delitem(map_session_data* sd, struct s_storage *stor, int index, int amount)
- {
- if( stor->u.items_storage[index].nameid == 0 || stor->u.items_storage[index].amount < amount )
- return 1;
- stor->u.items_storage[index].amount -= amount;
- stor->dirty = true;
- if( stor->u.items_storage[index].amount == 0 ) {
- memset(&stor->u.items_storage[index],0,sizeof(stor->u.items_storage[0]));
- stor->amount--;
- if( sd->state.storage_flag == 1 || sd->state.storage_flag == 3 )
- clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
- }
- if( sd->state.storage_flag == 1 || sd->state.storage_flag == 3 )
- clif_storageitemremoved( *sd, index, amount );
- return 0;
- }
- /**
- * Add an item to the storage from the inventory.
- * @param sd : player
- * @param stor : Storage data
- * @param index : inventory index to take the item from
- * @param amount : number of item to take
- * @return 0:fail, 1:success
- */
- void storage_storageadd(map_session_data* sd, struct s_storage *stor, int index, int amount)
- {
- enum e_storage_add result;
- nullpo_retv(sd);
- result = storage_canAddItem(stor, index, sd->inventory.u.items_inventory, amount, MAX_INVENTORY);
- if (result == STORAGE_ADD_INVALID)
- return;
- else if (result == STORAGE_ADD_OK) {
- switch( storage_additem(sd, stor, &sd->inventory.u.items_inventory[index], amount) ){
- case 0:
- pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE);
- return;
- case 1:
- break;
- case 2:
- result = STORAGE_ADD_NOROOM;
- break;
- }
- }
- clif_storageitemremoved( *sd, index, 0 );
- clif_dropitem( *sd, index, 0 );
- }
- /**
- * Retrieve an item from the storage into inventory
- * @param sd : player
- * @param index : storage index to take the item from
- * @param amount : number of item to take
- * @return 0:fail, 1:success
- */
- void storage_storageget(map_session_data *sd, struct s_storage *stor, int index, int amount)
- {
- unsigned char flag = 0;
- enum e_storage_add result;
- nullpo_retv(sd);
- result = storage_canGetItem(stor, index, amount);
- if (result != STORAGE_ADD_OK)
- return;
- if ((flag = pc_additem(sd,&stor->u.items_storage[index],amount,LOG_TYPE_STORAGE)) == ADDITEM_SUCCESS)
- storage_delitem(sd,stor,index,amount);
- else {
- clif_storageitemremoved( *sd, index, 0 );
- clif_additem(sd,0,0,flag);
- }
- }
- /**
- * Move an item from cart to storage.
- * @param sd : player
- * @param stor : Storage data
- * @param index : cart index to take the item from
- * @param amount : number of item to take
- * @return 0:fail, 1:success
- */
- void storage_storageaddfromcart(map_session_data *sd, struct s_storage *stor, int index, int amount)
- {
- enum e_storage_add result;
- nullpo_retv(sd);
- if (sd->state.prevend) {
- return;
- }
- result = storage_canAddItem(stor, index, sd->cart.u.items_cart, amount, MAX_CART);
- if (result == STORAGE_ADD_INVALID)
- return;
- else if (result == STORAGE_ADD_OK) {
- switch( storage_additem(sd, stor, &sd->cart.u.items_cart[index], amount) ){
- case 0:
- pc_cart_delitem(sd,index,amount,0,LOG_TYPE_STORAGE);
- return;
- case 1:
- break;
- case 2:
- result = STORAGE_ADD_NOROOM;
- break;
- }
- }
- clif_storageitemremoved( *sd, index, 0 );
- clif_dropitem( *sd, index, 0 );
- }
- /**
- * Get from Storage to the Cart inventory
- * @param sd : player
- * @param stor : Storage data
- * @param index : storage index to take the item from
- * @param amount : number of item to take
- * @return 0:fail, 1:success
- */
- void storage_storagegettocart(map_session_data* sd, struct s_storage *stor, int index, int amount)
- {
- unsigned char flag = 0;
- enum e_storage_add result;
- nullpo_retv(sd);
- if (sd->state.prevend) {
- return;
- }
- result = storage_canGetItem(stor, index, amount);
- if (result != STORAGE_ADD_OK)
- return;
- if ((flag = pc_cart_additem(sd,&stor->u.items_storage[index],amount,LOG_TYPE_STORAGE)) == 0)
- storage_delitem(sd,stor,index,amount);
- else {
- clif_storageitemremoved( *sd, index, 0 );
- if (flag == ADDITEM_INVALID)
- clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_WEIGHT );
- else
- clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_COUNT );
- }
- }
- /**
- * Request to save storage
- * @param sd: Player who has the storage
- */
- void storage_storagesave(map_session_data *sd)
- {
- nullpo_retv(sd);
- intif_storage_save(sd, &sd->storage);
- }
- /**
- * Make player close his storage
- * @param sd: Player who has the storage
- * @author [massdriller] / modified by [Valaris]
- */
- void storage_storageclose(map_session_data *sd)
- {
- nullpo_retv(sd);
- if (sd->storage.dirty) {
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- else
- storage_storagesave(sd);
- }
-
- if( sd->state.storage_flag == 1 ){
- sd->state.storage_flag = 0;
- clif_storageclose( *sd );
- }
- }
- /**
- * Force closing the storage for player without displaying result
- * (example when quitting the game)
- * @param sd : player to close storage
- * @param flag :
- * 1: Character is quitting
- * 2(x): Character is changing map-servers
- */
- void storage_storage_quit(map_session_data* sd, int flag)
- {
- nullpo_retv(sd);
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- else
- storage_storagesave(sd);
- }
- /**
- * Retrieve the guild_storage of a guild
- * will create a new storage if none found for the guild
- * @param guild_id : id of the guild
- * @return s_storage
- */
- struct s_storage *guild2storage(int guild_id)
- {
- struct s_storage *gs;
- if (guild_search(guild_id) == nullptr)
- return nullptr;
- gs = guild2storage2(guild_id);
-
- if( gs == nullptr ){
- gs = &guild_storage_db[guild_id];
- gs->id = guild_id;
- gs->type = TABLE_GUILD_STORAGE;
- }
- return gs;
- }
- /**
- * See if the guild_storage exist in db and fetch it if it's the case
- * @author : [Skotlex]
- * @param guild_id : guild_id to search the storage
- * @return s_storage or nullptr
- */
- struct s_storage *guild2storage2(int guild_id){
- return util::map_find( guild_storage_db, guild_id );
- }
- /**
- * Delete a guild_storage and remove it from db
- * @param guild_id : guild to remove the storage from
- */
- void storage_guild_delete(int guild_id)
- {
- guild_storage_db.erase(guild_id);
- }
- /**
- * Attempt to open guild storage for player
- * @param sd : player
- * @return 0 : success, 1 : fail, 2 : no guild found
- */
- char storage_guild_storageopen(map_session_data* sd)
- {
- struct s_storage *gstor;
- nullpo_ret(sd);
- if(sd->status.guild_id <= 0)
- return GSTORAGE_NO_GUILD;
- #ifdef OFFICIAL_GUILD_STORAGE
- if (!guild_checkskill(sd->guild->guild, GD_GUILD_STORAGE))
- return GSTORAGE_NO_STORAGE; // Can't open storage if the guild has not learned the skill
- #endif
- if (sd->state.storage_flag == 2)
- return GSTORAGE_ALREADY_OPEN; // Guild storage already open.
- else if (sd->state.storage_flag)
- return GSTORAGE_STORAGE_ALREADY_OPEN; // Can't open both storages at a time.
- #if PACKETVER >= 20140205
- int pos;
- if ((pos = guild_getposition(*sd)) < 0 || !(sd->guild->guild.position[pos].mode&GUILD_PERM_STORAGE))
- return GSTORAGE_NO_PERMISSION; // Guild member doesn't have permission
- #endif
- if( !pc_can_give_items(sd) ) { //check is this GM level can open guild storage and store items [Lupus]
- clif_displaymessage(sd->fd, msg_txt(sd,246));
- return GSTORAGE_ALREADY_OPEN;
- }
- if((gstor = guild2storage2(sd->status.guild_id)) == nullptr
- #ifdef OFFICIAL_GUILD_STORAGE
- || (gstor->max_amount != guild_checkskill(sd->guild->guild, GD_GUILD_STORAGE) * 100)
- #endif
- ) {
- intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
- return GSTORAGE_OPEN;
- }
- if(gstor->status)
- return GSTORAGE_ALREADY_OPEN;
- if( gstor->lock )
- return GSTORAGE_ALREADY_OPEN;
- gstor->status = true;
- sd->state.storage_flag = 2;
- storage_sortitem(gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild));
- clif_storagelist(sd, gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild), "Guild Storage");
- clif_updatestorageamount(*sd, gstor->amount, gstor->max_amount);
- return GSTORAGE_OPEN;
- }
- void storage_guild_log( map_session_data* sd, struct item* item, int16 amount ){
- int i;
- SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
- StringBuf buf;
- StringBuf_Init(&buf);
- 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);
- for (i = 0; i < MAX_SLOTS; ++i)
- StringBuf_Printf(&buf, ", `card%d`", i);
- for (i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
- StringBuf_Printf(&buf, ", `option_id%d`", i);
- StringBuf_Printf(&buf, ", `option_val%d`", i);
- StringBuf_Printf(&buf, ", `option_parm%d`", i);
- }
- StringBuf_Printf(&buf, ") VALUES(NOW(),'%u','%u', '%s', '%u', '%d','%d','%d','%d','%" PRIu64 "','%d','%d'",
- 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);
- for (i = 0; i < MAX_SLOTS; i++)
- StringBuf_Printf(&buf, ",'%u'", item->card[i]);
- for (i = 0; i < MAX_ITEM_RDM_OPT; i++)
- StringBuf_Printf(&buf, ",'%d','%d','%d'", item->option[i].id, item->option[i].value, item->option[i].param);
- StringBuf_Printf(&buf, ")");
- if (SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) || SQL_SUCCESS != SqlStmt_Execute(stmt))
- SqlStmt_ShowDebug(stmt);
- SqlStmt_Free(stmt);
- StringBuf_Destroy(&buf);
- }
- enum e_guild_storage_log storage_guild_log_read_sub( map_session_data* sd, std::vector<struct guild_log_entry>& log, uint32 max ){
- StringBuf buf;
- int j;
- StringBuf_Init(&buf);
- StringBuf_AppendStr(&buf, "SELECT `id`, `time`, `name`, `amount`");
- StringBuf_AppendStr(&buf, " , `nameid`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`, `enchantgrade`");
- for (j = 0; j < MAX_SLOTS; ++j)
- StringBuf_Printf(&buf, ", `card%d`", j);
- for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
- StringBuf_Printf(&buf, ", `option_id%d`", j);
- StringBuf_Printf(&buf, ", `option_val%d`", j);
- StringBuf_Printf(&buf, ", `option_parm%d`", j);
- }
- StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%u'", guild_storage_log_table, sd->status.guild_id );
- StringBuf_Printf(&buf, " ORDER BY `time` DESC LIMIT %u", max);
- SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
- if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) ||
- SQL_ERROR == SqlStmt_Execute(stmt) )
- {
- SqlStmt_ShowDebug(stmt);
- SqlStmt_Free(stmt);
- StringBuf_Destroy(&buf);
- return GUILDSTORAGE_LOG_FAILED;
- }
- struct guild_log_entry entry;
- // General data
- SqlStmt_BindColumn(stmt, 0, SQLDT_UINT, &entry.id, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 1, SQLDT_STRING, &entry.time, sizeof(entry.time), nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &entry.name, sizeof(entry.name), nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 3, SQLDT_SHORT, &entry.amount, 0, nullptr, nullptr);
- // Item data
- SqlStmt_BindColumn(stmt, 4, SQLDT_UINT, &entry.item.nameid, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &entry.item.identify, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &entry.item.refine, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 7, SQLDT_CHAR, &entry.item.attribute, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 8, SQLDT_UINT, &entry.item.expire_time, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 9, SQLDT_UINT, &entry.item.bound, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 10, SQLDT_UINT64, &entry.item.unique_id, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 11, SQLDT_INT8, &entry.item.enchantgrade,0, nullptr, nullptr);
- for( j = 0; j < MAX_SLOTS; ++j )
- SqlStmt_BindColumn(stmt, 12+j, SQLDT_UINT, &entry.item.card[j], 0, nullptr, nullptr);
- for( j = 0; j < MAX_ITEM_RDM_OPT; ++j ) {
- SqlStmt_BindColumn(stmt, 12+MAX_SLOTS+j*3, SQLDT_SHORT, &entry.item.option[j].id, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 12+MAX_SLOTS+j*3+1, SQLDT_SHORT, &entry.item.option[j].value, 0, nullptr, nullptr);
- SqlStmt_BindColumn(stmt, 12+MAX_SLOTS+j*3+2, SQLDT_CHAR, &entry.item.option[j].param, 0, nullptr, nullptr);
- }
- log.reserve(max);
- while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ){
- log.push_back( entry );
- }
- Sql_FreeResult(mmysql_handle);
- StringBuf_Destroy(&buf);
- SqlStmt_Free(stmt);
- if( log.empty() ){
- return GUILDSTORAGE_LOG_EMPTY;
- }
- return GUILDSTORAGE_LOG_FINAL_SUCCESS;
- }
- enum e_guild_storage_log storage_guild_log_read( map_session_data* sd ){
- std::vector<struct guild_log_entry> log;
- enum e_guild_storage_log ret = storage_guild_log_read_sub( sd, log, MAX_GUILD_STORAGE_LOG_PACKET );
- clif_guild_storage_log( sd, log, ret );
- return ret;
- }
- /**
- * Attempt to add an item in guild storage, then refresh it
- * @param sd : player attempting to open the guild_storage
- * @param stor : guild_storage
- * @param item : item to add
- * @param amount : number of item to add
- * @return True : success, False : fail
- */
- bool storage_guild_additem(map_session_data* sd, struct s_storage* stor, struct item* item_data, int amount)
- {
- struct item_data *id;
- int i;
- nullpo_ret(sd);
- nullpo_ret(stor);
- nullpo_ret(item_data);
- if(item_data->nameid == 0 || amount <= 0)
- return false;
- id = itemdb_search(item_data->nameid);
- if( id->stack.guild_storage && amount > id->stack.amount ) // item stack limitation
- return false;
- if (!itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time) { // Check if item is storable. [Skotlex]
- clif_displaymessage (sd->fd, msg_txt(sd,264));
- return false;
- }
- if ((item_data->bound == BOUND_ACCOUNT || item_data->bound > BOUND_GUILD) && !pc_can_give_bounded_items(sd)) {
- clif_displaymessage(sd->fd, msg_txt(sd,294));
- return false;
- }
- if(itemdb_isstackable2(id)) { //Stackable
- for(i = 0; i < stor->max_amount; i++) {
- if(compare_item(&stor->u.items_guild[i], item_data)) {
- if( amount > MAX_AMOUNT - stor->u.items_guild[i].amount || ( id->stack.guild_storage && amount > id->stack.amount - stor->u.items_guild[i].amount ) )
- return false;
- stor->u.items_guild[i].amount += amount;
- clif_storageitemadded(sd,&stor->u.items_guild[i],i,amount);
- stor->dirty = true;
- storage_guild_log( sd, &stor->u.items_guild[i], amount );
- return true;
- }
- }
- }
- //Add item
- for(i = 0; i < stor->max_amount && stor->u.items_guild[i].nameid; i++);
- if(i >= stor->max_amount)
- return false;
- memcpy(&stor->u.items_guild[i],item_data,sizeof(stor->u.items_guild[0]));
- stor->u.items_guild[i].amount = amount;
- stor->amount++;
- clif_storageitemadded(sd,&stor->u.items_guild[i],i,amount);
- clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
- stor->dirty = true;
- storage_guild_log( sd, &stor->u.items_guild[i], amount );
- return true;
- }
- /**
- * Attempt to add an item in guild storage, then refresh i
- * @param stor : guild_storage
- * @param item : item to add
- * @param amount : number of item to add
- * @return True : success, False : fail
- */
- bool storage_guild_additem2(struct s_storage* stor, struct item* item, int amount) {
- int i;
- nullpo_ret(stor);
- nullpo_ret(item);
- if (item->nameid == 0 || amount <= 0)
- return false;
- std::shared_ptr<item_data> id = item_db.find(item->nameid);
- if (id == nullptr || item->expire_time)
- return false;
- if (itemdb_isstackable2(id.get())) { // Stackable
- for (i = 0; i < stor->max_amount; i++) {
- if (compare_item(&stor->u.items_guild[i], item)) {
- // Set the amount, make it fit with max amount
- amount = min(amount, ((id->stack.guild_storage) ? id->stack.amount : MAX_AMOUNT) - stor->u.items_guild[i].amount);
- if (amount != item->amount)
- 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);
- stor->u.items_guild[i].amount += amount;
- stor->dirty = true;
- return true;
- }
- }
- }
- // Add the item
- for (i = 0; i < stor->max_amount && stor->u.items_guild[i].nameid; i++);
- if (i >= stor->max_amount)
- return false;
- memcpy(&stor->u.items_guild[i], item, sizeof(stor->u.items_guild[0]));
- stor->u.items_guild[i].amount = amount;
- stor->amount++;
- stor->dirty = true;
- return true;
- }
- /**
- * Attempt to delete an item in guild storage, then refresh it
- * @param sd : player
- * @param stor : guild_storage
- * @param n : index of item in guild storage
- * @param amount : number of item to delete
- * @return True : success, False : fail
- */
- bool storage_guild_delitem(map_session_data* sd, struct s_storage* stor, int n, int amount)
- {
- nullpo_retr(1, sd);
- nullpo_retr(1, stor);
- if(!stor->u.items_guild[n].nameid || stor->u.items_guild[n].amount < amount)
- return false;
- // Log before removing it
- storage_guild_log( sd, &stor->u.items_guild[n], -amount );
- stor->u.items_guild[n].amount -= amount;
- if(!stor->u.items_guild[n].amount) {
- memset(&stor->u.items_guild[n],0,sizeof(stor->u.items_guild[0]));
- stor->amount--;
- clif_updatestorageamount(*sd, stor->amount, stor->max_amount);
- }
- clif_storageitemremoved( *sd, n, amount );
- stor->dirty = true;
- return true;
- }
- /**
- * Attempt to add an item in guild storage from inventory, then refresh it
- * @param sd : player
- * @param amount : number of item to delete
- */
- void storage_guild_storageadd(map_session_data* sd, int index, int amount)
- {
- struct s_storage *stor;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- if( !stor->status || stor->amount > stor->max_amount )
- return;
- if( index < 0 || index >= MAX_INVENTORY )
- return;
- if( sd->inventory.u.items_inventory[index].nameid == 0 )
- return;
- if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount )
- return;
- if (itemdb_ishatched_egg(&sd->inventory.u.items_inventory[index]))
- return;
- if( stor->lock ) {
- storage_guild_storageclose(sd);
- return;
- }
- if(storage_guild_additem(sd,stor,&sd->inventory.u.items_inventory[index],amount))
- pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE);
- else {
- clif_storageitemremoved( *sd, index, 0 );
- clif_dropitem( *sd, index, 0 );
- }
- }
- /**
- * Attempt to retrieve an item from guild storage to inventory, then refresh it
- * @param sd : player
- * @param index : index of item in storage
- * @param amount : number of item to get
- * @return 1:success, 0:fail
- */
- void storage_guild_storageget(map_session_data* sd, int index, int amount)
- {
- struct s_storage *stor;
- unsigned char flag = 0;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- if(!stor->status)
- return;
- if(index < 0 || index >= stor->max_amount)
- return;
- if(stor->u.items_guild[index].nameid == 0)
- return;
- if(amount < 1 || amount > stor->u.items_guild[index].amount)
- return;
- if( stor->lock ) {
- storage_guild_storageclose(sd);
- return;
- }
- if((flag = pc_additem(sd,&stor->u.items_guild[index],amount,LOG_TYPE_GSTORAGE)) == 0)
- storage_guild_delitem(sd,stor,index,amount);
- else { // inform fail
- clif_storageitemremoved( *sd, index, 0 );
- clif_additem(sd,0,0,flag);
- }
- }
- /**
- * Attempt to add an item in guild storage from cart, then refresh it
- * @param sd : player
- * @param index : index of item in cart
- * @param amount : number of item to transfer
- */
- void storage_guild_storageaddfromcart(map_session_data* sd, int index, int amount)
- {
- struct s_storage *stor;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- if( !stor->status || stor->amount > stor->max_amount )
- return;
- if( index < 0 || index >= MAX_CART )
- return;
- if( sd->cart.u.items_cart[index].nameid == 0 )
- return;
- if( amount < 1 || amount > sd->cart.u.items_cart[index].amount )
- return;
- if(storage_guild_additem(sd,stor,&sd->cart.u.items_cart[index],amount))
- pc_cart_delitem(sd,index,amount,0,LOG_TYPE_GSTORAGE);
- else {
- clif_storageitemremoved( *sd, index, 0 );
- clif_dropitem( *sd, index, 0 );
- }
- }
- /**
- * Attempt to retrieve an item from guild storage to cart, then refresh it
- * @param sd : player
- * @param index : index of item in storage
- * @param amount : number of item to transfer
- * @return 1:fail, 0:success
- */
- void storage_guild_storagegettocart(map_session_data* sd, int index, int amount)
- {
- short flag;
- struct s_storage *stor;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- if(!stor->status)
- return;
- if(index < 0 || index >= stor->max_amount)
- return;
- if(stor->u.items_guild[index].nameid == 0)
- return;
- if(amount < 1 || amount > stor->u.items_guild[index].amount)
- return;
- if((flag = pc_cart_additem(sd,&stor->u.items_guild[index],amount,LOG_TYPE_GSTORAGE)) == 0)
- storage_guild_delitem(sd,stor,index,amount);
- else {
- clif_storageitemremoved( *sd, index, 0 );
- if (flag == ADDITEM_INVALID)
- clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_WEIGHT );
- else
- clif_cart_additem_ack( *sd, ADDITEM_TO_CART_FAIL_COUNT );
- }
- }
- /**
- * Request to save guild storage
- * @param account_id : account requesting the save
- * @param guild_id : guild to take the guild_storage
- * @param flag : 1=char quitting, close the storage
- * @return False : fail (no storage), True : success (requested)
- */
- bool storage_guild_storagesave(uint32 account_id, int guild_id, int flag)
- {
- struct s_storage *stor = guild2storage2(guild_id);
- if (stor) {
- if (flag&CSAVE_QUIT) //Char quitting, close it.
- stor->status = false;
- if (stor->dirty)
- intif_send_guild_storage(account_id,stor);
- return true;
- }
- return false;
- }
- /**
- * ACK save of guild storage
- * @param guild_id : guild to use the storage
- */
- void storage_guild_storagesaved(int guild_id)
- {
- struct s_storage *stor;
- if ((stor = guild2storage2(guild_id)) != nullptr) {
- if (stor->dirty && !stor->status) // Storage has been correctly saved.
- stor->dirty = false;
- }
- }
- /**
- * Close storage for player then save it
- * @param sd : player
- */
- void storage_guild_storageclose(map_session_data* sd)
- {
- struct s_storage *stor;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- clif_storageclose( *sd );
- if (stor->status) {
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART); //This one also saves the storage. [Skotlex]
- else
- storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0);
- stor->status = false;
- }
- sd->state.storage_flag = 0;
- }
- /**
- * Close storage for player then save it
- * @param sd
- * @param flag
- */
- void storage_guild_storage_quit(map_session_data* sd, int flag)
- {
- struct s_storage *stor;
- nullpo_retv(sd);
- nullpo_retv(stor = guild2storage2(sd->status.guild_id));
- if (flag) { //Only during a guild break flag is 1 (don't save storage)
- clif_storageclose( *sd );
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- sd->state.storage_flag = 0;
- stor->status = false;
- return;
- }
- if (stor->status) {
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- else
- storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1);
- }
- sd->state.storage_flag = 0;
- stor->status = false;
- }
- /**
- * Open premium storage
- * @param sd Player
- **/
- void storage_premiumStorage_open(map_session_data *sd) {
- nullpo_retv(sd);
- sd->state.storage_flag = 3;
- storage_sortitem(sd->premiumStorage.u.items_storage, ARRAYLENGTH(sd->premiumStorage.u.items_storage));
- clif_storagelist(sd, sd->premiumStorage.u.items_storage, ARRAYLENGTH(sd->premiumStorage.u.items_storage), storage_getName(sd->premiumStorage.stor_id));
- clif_updatestorageamount(*sd, sd->premiumStorage.amount, sd->premiumStorage.max_amount);
- }
- /**
- * Request to open premium storage
- * @param sd Player who request
- * @param num Storage number
- * @param mode Storage mode @see enum e_storage_mode
- * @return 1:Success to request, 0:Failed
- * @author [Cydh]
- **/
- bool storage_premiumStorage_load(map_session_data *sd, uint8 num, uint8 mode) {
- nullpo_ret(sd);
- if (sd->state.storage_flag)
- return 0;
- if (sd->state.vending || sd->state.buyingstore || sd->state.prevend || sd->state.autotrade)
- return 0;
- if (sd->state.banking || sd->state.callshop)
- return 0;
- if (!pc_can_give_items(sd)) { // check is this GM level is allowed to put items to storage
- clif_displaymessage(sd->fd, msg_txt(sd,246));
- return 0;
- }
- if (sd->premiumStorage.stor_id != num)
- return intif_storage_request(sd, TABLE_STORAGE, num, mode);
- else {
- sd->premiumStorage.state.put = (mode&STOR_MODE_PUT) ? 1 : 0;
- sd->premiumStorage.state.get = (mode&STOR_MODE_GET) ? 1 : 0;
- storage_premiumStorage_open(sd);
- }
- return 1;
- }
- /**
- * Request to save premium storage
- * @param sd Player who has the storage
- * @author [Cydh]
- **/
- void storage_premiumStorage_save(map_session_data *sd) {
- nullpo_retv(sd);
- intif_storage_save(sd, &sd->premiumStorage);
- }
- /**
- * Request to close premium storage
- * @param sd Player who has the storage
- * @author [Cydh]
- **/
- void storage_premiumStorage_close(map_session_data *sd) {
- nullpo_retv(sd);
- if (sd->premiumStorage.dirty) {
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- else
- storage_premiumStorage_save(sd);
- }
- if( sd->state.storage_flag == 3 ){
- sd->state.storage_flag = 0;
- clif_storageclose( *sd );
- }
- }
- /**
- * Force save the premium storage
- * @param sd Player who has the storage
- * @author [Cydh]
- **/
- void storage_premiumStorage_quit(map_session_data *sd) {
- nullpo_retv(sd);
- if (save_settings&CHARSAVE_STORAGE)
- chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
- else
- storage_premiumStorage_save(sd);
- }
|