From 46b1de7d5074dc1dc80e17214e23acae656161a9 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Fri, 14 Oct 2016 08:49:32 +0700 Subject: [PATCH] Initial release 'premium' storage * Adapting official multiple storages. * Added config to defines storages. See conf/import-tmpl/inter_server.conf. * Added packet IZ `0x388c` for sending storage definitions. * Added script command `openstorage2` to open premium storages. * Removed `sd->storage_size`, replaced by `sd->storage.max_amount`. The value is defined in inter_server.conf in storage entries. Signed-off-by: Cydh Ramdh --- conf/import-tmpl/inter_server.conf | 0 conf/inter_athena.conf | 3 +- conf/inter_server.conf | 25 ++ doc/packet_interserv.txt | 12 + doc/script_commands.txt | 27 ++ sql-files/upgrades/premium_storage.sql | 38 +++ src/char/char.c | 27 +- src/char/char.h | 6 +- src/char/int_storage.c | 123 +++++++-- src/char/int_storage.h | 4 + src/char/inter.c | 103 ++++++- src/char/inter.h | 11 + src/common/mmo.h | 23 +- src/map/atcommand.c | 20 +- src/map/chrif.c | 22 +- src/map/clif.c | 39 ++- src/map/clif.h | 2 +- src/map/intif.c | 108 +++++--- src/map/intif.h | 4 +- src/map/pc.c | 13 +- src/map/pc.h | 5 +- src/map/script.c | 24 +- src/map/script_constants.h | 4 + src/map/skill.c | 2 +- src/map/storage.c | 357 +++++++++++++++++++------ src/map/storage.h | 31 ++- src/map/unit.c | 2 + vcproj-10/map-server.vcxproj | 1 + vcproj-12/map-server.vcxproj | 1 + vcproj-13/map-server.vcxproj | 1 + vcproj-14/map-server.vcxproj | 1 + 31 files changed, 843 insertions(+), 196 deletions(-) create mode 100644 conf/import-tmpl/inter_server.conf create mode 100644 conf/inter_server.conf create mode 100644 sql-files/upgrades/premium_storage.sql diff --git a/conf/import-tmpl/inter_server.conf b/conf/import-tmpl/inter_server.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/conf/inter_athena.conf b/conf/inter_athena.conf index 2a8d642a17..9fc1c1e1f4 100644 --- a/conf/inter_athena.conf +++ b/conf/inter_athena.conf @@ -93,7 +93,6 @@ scdata_db: sc_data cart_db: cart_inventory inventory_db: inventory charlog_db: charlog -storage_db: storage skill_db: skill interlog_db: interlog memo_db: memo @@ -150,4 +149,6 @@ db_roulette_table: db_roulette // Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no) use_sql_db: no +inter_server_conf: conf/inter_server.conf + import: conf/import/inter_conf.txt diff --git a/conf/inter_server.conf b/conf/inter_server.conf new file mode 100644 index 0000000000..448b14d05f --- /dev/null +++ b/conf/inter_server.conf @@ -0,0 +1,25 @@ +/** + * Config for storages + * + * To access premium storage, use 'openstorage2' script command (except for the default storage). + * If premium storages are added, just copy the structure of storage table then match the table name in this config. + * The 'max' of premium storages are not adjusted by 'vip_storage_increase' config nor MIN_STORAGE. + * + * Structure: +{ + id: // (int) Storage ID will be used for 'openstorage2' script command. + name: "" // (string) Storage name will be sent to the client when player open the storage. + table: "" // (string) Name of table where storage is saved, the table stucture is same like default storage name + max: // (int) *optional* Maximum number of storage, MAX_STORAGE will be used if no value defined +}, // Use comma to add more storages + **/ +storages: ( +{ + // Default storage + // DO NOT CHANGE THIS EXCEPT YOU KNOW WHAT YOU ARE DOING + id: 0 + name: "Storage" + table: "storage" + //max: 600 +} +) diff --git a/doc/packet_interserv.txt b/doc/packet_interserv.txt index 5ad05e2b3e..286a6d769c 100644 --- a/doc/packet_interserv.txt +++ b/doc/packet_interserv.txt @@ -2194,6 +2194,18 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket. desc: - Info about inventory/cart/storage data is saved +0x388c + Type: IZ + Structure: .W .W { .? }*? + index: 0,2,6,... + len: 6+variable + parameter: + - cmd : packet identification (0x388c) + - len : Pakcet length + - storage_table : Storage table information + desc: + - Receive storage information + 0x3890 Type: IZ Structure: .W .W .L .B .?B diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 90ff56effd..b75e802804 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -5053,6 +5053,33 @@ window, to avoid any disruption when both windows overlap. --------------------------------------- +*openstorage2 ,{,}; + +Just like 'openstorage' command, except this command can open additional storage +by specified 'storage_id'. For storage_id, please read the conf/inter_server.conf +for storages groups. + +Values for 'mode' are: + STOR_MODE_NONE : Player only can read the storage entries. + STOR_MODE_GET : Player can get items from the storage. + STOR_MODE_PUT : Player can put items to the storage. + +Example: + if (vip_status(1)) { + mes "I will open your Premium storage."; + mes "Thank you for using our service."; + close2; + openstorage2 1,STOR_MODE_GET|STOR_MODE_PUT; + } else { + mes "Sorry, your Premium status is expired."; + mes "Storage will be opened but you can't put any item into it."; + close2; + openstorage2 1,STOR_MODE_GET; + } + end; + +--------------------------------------- + *openmail({}); This will open a character's Mail window on the client connected to the diff --git a/sql-files/upgrades/premium_storage.sql b/sql-files/upgrades/premium_storage.sql new file mode 100644 index 0000000000..1718374c4a --- /dev/null +++ b/sql-files/upgrades/premium_storage.sql @@ -0,0 +1,38 @@ +-- +-- Table structure for table `storage_1` +-- + +CREATE TABLE IF NOT EXISTS `storage_1` ( + `id` int(11) unsigned NOT NULL auto_increment, + `account_id` int(11) unsigned NOT NULL default '0', + `nameid` smallint(5) unsigned NOT NULL default '0', + `amount` smallint(11) unsigned NOT NULL default '0', + `equip` int(11) unsigned NOT NULL default '0', + `identify` smallint(6) unsigned NOT NULL default '0', + `refine` tinyint(3) unsigned NOT NULL default '0', + `attribute` tinyint(4) unsigned NOT NULL default '0', + `card0` smallint(5) unsigned NOT NULL default '0', + `card1` smallint(5) unsigned NOT NULL default '0', + `card2` smallint(5) unsigned NOT NULL default '0', + `card3` smallint(5) unsigned NOT NULL default '0', + `option_id0` smallint(5) unsigned NOT NULL default '0', + `option_val0` smallint(5) unsigned NOT NULL default '0', + `option_parm0` tinyint(3) unsigned NOT NULL default '0', + `option_id1` smallint(5) unsigned NOT NULL default '0', + `option_val1` smallint(5) unsigned NOT NULL default '0', + `option_parm1` tinyint(3) unsigned NOT NULL default '0', + `option_id2` smallint(5) unsigned NOT NULL default '0', + `option_val2` smallint(5) unsigned NOT NULL default '0', + `option_parm2` tinyint(3) unsigned NOT NULL default '0', + `option_id3` smallint(5) unsigned NOT NULL default '0', + `option_val3` smallint(5) unsigned NOT NULL default '0', + `option_parm3` tinyint(3) unsigned NOT NULL default '0', + `option_id4` smallint(5) unsigned NOT NULL default '0', + `option_val4` smallint(5) unsigned NOT NULL default '0', + `option_parm4` tinyint(3) unsigned NOT NULL default '0', + `expire_time` int(11) unsigned NOT NULL default '0', + `bound` tinyint(3) unsigned NOT NULL default '0', + `unique_id` bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY (`id`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; diff --git a/src/char/char.c b/src/char/char.c index 1b7100cc2f..9904e92f87 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -23,6 +23,7 @@ #include "int_mercenary.h" #include "int_elemental.h" #include "int_party.h" +#include "int_storage.h" #include "inter.h" #include "char_logif.h" #include "char_mapif.h" @@ -515,7 +516,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ } /// Saves an array of 'item' entries into the specified table. -int char_memitemdata_to_sql(const struct item items[], int max, int id, enum storage_type tableswitch) { +int char_memitemdata_to_sql(const struct item items[], int max, int id, enum storage_type tableswitch, uint8 stor_id) { StringBuf buf; SqlStmt* stmt; int i, j, offset = 0, errors = 0; @@ -537,7 +538,7 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, enum sto break; case TABLE_STORAGE: printname = "Storage"; - tablename = schema_config.storage_db; + tablename = inter_premiumStorage_getTableName(stor_id); selectoption = "account_id"; break; case TABLE_GUILD_STORAGE: @@ -720,15 +721,14 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, enum sto errors++; } - ShowInfo("Saved %s data for %s: %d\n", printname, selectoption, id); - + ShowInfo("Saved %s (%d) data to table %s for %s: %d\n", printname, stor_id, tablename, selectoption, id); StringBuf_Destroy(&buf); aFree(flag); return errors; } -bool char_memitemdata_from_sql( struct s_storage* p, int max, int id, enum storage_type tableswitch ){ +bool char_memitemdata_from_sql(struct s_storage* p, int max, int id, enum storage_type tableswitch, uint8 stor_id) { StringBuf buf; SqlStmt* stmt; int i,j, offset = 0; @@ -750,7 +750,7 @@ bool char_memitemdata_from_sql( struct s_storage* p, int max, int id, enum stora break; case TABLE_STORAGE: printname = "Storage"; - tablename = schema_config.storage_db; + tablename = inter_premiumStorage_getTableName(stor_id); selectoption = "account_id"; storage = p->u.items_storage; break; @@ -768,6 +768,8 @@ bool char_memitemdata_from_sql( struct s_storage* p, int max, int id, enum stora memset(p, 0, sizeof(struct s_storage)); //clean up memory p->id = id; p->type = tableswitch; + p->stor_id = stor_id; + p->max_amount = inter_premiumStorage_getMax(p->stor_id); stmt = SqlStmt_Malloc(sql_handle); if (stmt == NULL) { @@ -824,7 +826,7 @@ bool char_memitemdata_from_sql( struct s_storage* p, int max, int id, enum stora memcpy(&storage[i], &item, sizeof(item)); p->amount = i; - ShowInfo("Loaded %s data from DB for %s: %d (total: %d)\n", printname, selectoption, id, p->amount); + ShowInfo("Loaded %s (%d) data from table %s for %s: %d (total: %d)\n", printname, p->stor_id, tablename, selectoption, id, p->amount); SqlStmt_FreeResult(stmt); SqlStmt_Free(stmt); @@ -2181,7 +2183,7 @@ bool char_checkdb(void){ int i; const char* sqltable[] = { schema_config.char_db, schema_config.hotkey_db, schema_config.scdata_db, schema_config.cart_db, - schema_config.inventory_db, schema_config.charlog_db, schema_config.storage_db, + schema_config.inventory_db, schema_config.charlog_db, schema_config.char_reg_str_table, schema_config.char_reg_num_table, schema_config.acc_reg_str_table, schema_config.acc_reg_num_table, schema_config.skill_db, schema_config.interlog_db, schema_config.memo_db, schema_config.guild_db, schema_config.guild_alliance_db, schema_config.guild_castle_db, @@ -2404,13 +2406,6 @@ bool char_checkdb(void){ Sql_ShowDebug(sql_handle); return false; } - //checking storage_db - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`account_id`,`nameid`,`amount`,`equip`,`identify`,`refine`," - "`attribute`,`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`expire_time`,`bound`,`unique_id`" - " FROM `%s` LIMIT 1;", schema_config.storage_db) ){ - Sql_ShowDebug(sql_handle); - return false; - } //checking guild_storage_db if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`guild_id`,`nameid`,`amount`,`equip`,`identify`,`refine`," "`attribute`,`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`expire_time`,`bound`,`unique_id`" @@ -2449,8 +2444,6 @@ void char_sql_config_read(const char* cfgName) { safestrncpy(schema_config.inventory_db, w2, sizeof(schema_config.inventory_db)); else if(!strcmpi(w1,"charlog_db")) safestrncpy(schema_config.charlog_db, w2, sizeof(schema_config.charlog_db)); - else if(!strcmpi(w1,"storage_db")) - safestrncpy(schema_config.storage_db, w2, sizeof(schema_config.storage_db)); else if(!strcmpi(w1,"skill_db")) safestrncpy(schema_config.skill_db, w2, sizeof(schema_config.skill_db)); else if(!strcmpi(w1,"interlog_db")) diff --git a/src/char/char.h b/src/char/char.h index 5829388a6b..2886a8c886 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -4,8 +4,6 @@ #ifndef _CHAR_SQL_H_ #define _CHAR_SQL_H_ -#define DB_NAME_LEN 256 //max len of dbs - #include "../config/core.h" #include "../common/core.h" // CORE_ST_LAST #include "../common/msg_conf.h" @@ -264,8 +262,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf); int char_delete_char_sql(uint32 char_id); int char_rename_char_sql(struct char_session_data *sd, uint32 char_id); int char_divorce_char_sql(int partner_id1, int partner_id2); -int char_memitemdata_to_sql(const struct item items[], int max, int id, enum storage_type tableswitch); -bool char_memitemdata_from_sql( struct s_storage* p, int max, int id, enum storage_type tableswitch ); +int char_memitemdata_to_sql(const struct item items[], int max, int id, enum storage_type tableswitch, uint8 stor_id); +bool char_memitemdata_from_sql(struct s_storage* p, int max, int id, enum storage_type tableswitch, uint8 stor_id); void disconnect_player(uint32 account_id); diff --git a/src/char/int_storage.c b/src/char/int_storage.c index 55f6b3b3fe..91fe118715 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -1,6 +1,7 @@ // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder +#include "../common/malloc.h" #include "../common/mmo.h" #include "../common/showmsg.h" #include "../common/socket.h" @@ -14,6 +15,54 @@ #define STORAGE_MEMINC 16 +/** + * Check if sotrage ID is valid + * @param id Storage ID + * @return True:Valid, False:Invalid + **/ +bool inter_premiumStorage_exists(uint8 id) { + if (interserv_config.storages && interserv_config.storage_count) { + int i; + for (i = 0; i < interserv_config.storage_count; i++) { + if (interserv_config.storages[i].id == id) + return true; + } + } + return false; +} + +/** + * Get max storage amount + * @param id Storage ID + * @return Max amount + **/ +int inter_premiumStorage_getMax(uint8 id) { + if (interserv_config.storages && interserv_config.storage_count) { + int i; + for (i = 0; i < interserv_config.storage_count; i++) { + if (&interserv_config.storages[i] && interserv_config.storages[i].id == id) + return interserv_config.storages[i].max_num; + } + } + return MAX_STORAGE; +} + +/** + * Get table name of storage + * @param id Storage ID + * @return Table name + **/ +const char *inter_premiumStorage_getTableName(uint8 id) { + if (interserv_config.storages && interserv_config.storage_count) { + int i; + for (i = 0; i < interserv_config.storage_count; i++) { + if (&interserv_config.storages[i] && interserv_config.storages[i].id == id) + return interserv_config.storages[i].table; + } + } + return schema_config.storage_db; +} + /** * Save inventory entries to SQL * @param char_id: Character ID to save @@ -22,7 +71,7 @@ */ static int inventory_tosql(uint32 char_id, struct s_storage* p) { - return char_memitemdata_to_sql(p->u.items_inventory, MAX_INVENTORY, char_id, TABLE_INVENTORY); + return char_memitemdata_to_sql(p->u.items_inventory, MAX_INVENTORY, char_id, TABLE_INVENTORY, p->stor_id); } /** @@ -33,7 +82,7 @@ static int inventory_tosql(uint32 char_id, struct s_storage* p) */ static int storage_tosql(uint32 account_id, struct s_storage* p) { - return char_memitemdata_to_sql(p->u.items_storage, MAX_STORAGE, account_id, TABLE_STORAGE); + return char_memitemdata_to_sql(p->u.items_storage, MAX_STORAGE, account_id, TABLE_STORAGE, p->stor_id); } /** @@ -44,7 +93,7 @@ static int storage_tosql(uint32 account_id, struct s_storage* p) */ static int cart_tosql(uint32 char_id, struct s_storage* p) { - return char_memitemdata_to_sql(p->u.items_cart, MAX_CART, char_id, TABLE_CART); + return char_memitemdata_to_sql(p->u.items_cart, MAX_CART, char_id, TABLE_CART, p->stor_id); } /** @@ -55,7 +104,7 @@ static int cart_tosql(uint32 char_id, struct s_storage* p) */ static bool inventory_fromsql(uint32 char_id, struct s_storage* p) { - return char_memitemdata_from_sql( p, MAX_INVENTORY, char_id, TABLE_INVENTORY ); + return char_memitemdata_from_sql( p, MAX_INVENTORY, char_id, TABLE_INVENTORY, p->stor_id ); } /** @@ -66,18 +115,19 @@ static bool inventory_fromsql(uint32 char_id, struct s_storage* p) */ static bool cart_fromsql(uint32 char_id, struct s_storage* p) { - return char_memitemdata_from_sql( p, MAX_CART, char_id, TABLE_CART ); + return char_memitemdata_from_sql( p, MAX_CART, char_id, TABLE_CART, p->stor_id ); } /** * Fetch storage entries from table * @param char_id: Character ID to fetch * @param p: Storage list to save the entries + * @param stor_id: Storage ID * @return True if success, False if failed */ static bool storage_fromsql(uint32 account_id, struct s_storage* p) { - return char_memitemdata_from_sql( p, MAX_STORAGE, account_id, TABLE_STORAGE ); + return char_memitemdata_from_sql( p, MAX_STORAGE, account_id, TABLE_STORAGE, p->stor_id ); } /** @@ -89,7 +139,7 @@ static bool storage_fromsql(uint32 account_id, struct s_storage* p) bool guild_storage_tosql(int guild_id, struct s_storage* p) { //ShowInfo("Guild Storage has been saved (GID: %d)\n", guild_id); - return char_memitemdata_to_sql(p->u.items_guild, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE); + return char_memitemdata_to_sql(p->u.items_guild, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE, p->stor_id); } /** @@ -100,13 +150,31 @@ bool guild_storage_tosql(int guild_id, struct s_storage* p) */ bool guild_storage_fromsql(int guild_id, struct s_storage* p) { - return char_memitemdata_from_sql( p, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE ); + return char_memitemdata_from_sql( p, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE, p->stor_id ); +} + +static void inter_storage_checkDB(void) { + int i = 0; + // Checking storage tables + for (i = 0; i < interserv_config.storage_count; i++) { + if (!&interserv_config.storages[i] || !interserv_config.storages[i].name || !interserv_config.storages[i].table || interserv_config.storages[i].table == '\0') + continue; + if (SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`account_id`,`nameid`,`amount`,`equip`,`identify`,`refine`," + "`attribute`,`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`," + "`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`," + "`expire_time`,`bound`,`unique_id`" + " FROM `%s` LIMIT 1;", interserv_config.storages[i].table) ){ + Sql_ShowDebug(sql_handle); + } + } + Sql_FreeResult(sql_handle); } //--------------------------------------------------------- // storage data initialize void inter_storage_sql_init(void) { + inter_storage_checkDB(); return; } @@ -423,41 +491,56 @@ static void mapif_storage_data_loaded(int fd, uint32 account_id, char type, stru * @param char_id * @param success * @param type + * @param stor_id */ -void mapif_storage_saved(int fd, uint32 account_id, uint32 char_id, bool success, char type) { - WFIFOHEAD(fd,8); +void mapif_storage_saved(int fd, uint32 account_id, uint32 char_id, bool success, char type, uint8 stor_id) { + WFIFOHEAD(fd,9); WFIFOW(fd, 0) = 0x388b; WFIFOL(fd, 2) = account_id; WFIFOB(fd, 6) = success; WFIFOB(fd, 7) = type; - WFIFOSET(fd,8); + WFIFOB(fd, 8) = stor_id; + WFIFOSET(fd,9); } /** * Requested inventory/cart/storage data for a player - * ZI 0x308a .B .L .L + * ZI 0x308a .B .L .L .B .B * @param fd */ bool mapif_parse_StorageLoad(int fd) { uint32 aid, cid; int type; + uint8 stor_id, mode; struct s_storage stor; bool res = true; - RFIFOHEAD(fd); type = RFIFOB(fd,2); aid = RFIFOL(fd,3); cid = RFIFOL(fd,7); + stor_id = RFIFOB(fd,11); memset(&stor, 0, sizeof(struct s_storage)); + stor.stor_id = stor_id; //ShowInfo("Loading storage for AID=%d.\n", aid); switch (type) { case TABLE_INVENTORY: res = inventory_fromsql(cid, &stor); break; - case TABLE_STORAGE: res = storage_fromsql(aid, &stor); break; + case TABLE_STORAGE: + if (!inter_premiumStorage_exists(stor_id)) { + ShowError("Invalid storage with id %d\n", stor_id); + return false; + } + res = storage_fromsql(aid, &stor); + break; case TABLE_CART: res = cart_fromsql(cid, &stor); break; default: return false; } + + mode = RFIFOB(fd, 12); + stor.state.put = (mode&STOR_MODE_PUT) ? 1 : 0; + stor.state.get = (mode&STOR_MODE_GET) ? 1 : 0; + mapif_storage_data_loaded(fd, aid, type, stor, res); return true; } @@ -482,11 +565,17 @@ bool mapif_parse_StorageSave(int fd) { //ShowInfo("Saving storage data for AID=%d.\n", aid); switch(type){ case TABLE_INVENTORY: inventory_tosql(cid, &stor); break; - case TABLE_STORAGE: storage_tosql(aid, &stor); break; - case TABLE_CART: cart_tosql(cid, &stor); break; + case TABLE_STORAGE: + if (!inter_premiumStorage_exists(stor.stor_id)) { + ShowError("Invalid storage with id %d\n", stor.stor_id); + return false; + } + storage_tosql(aid, &stor); + break; + case TABLE_CART: cart_tosql(cid, &stor); break; default: return false; } - mapif_storage_saved(fd, aid, cid, true, type); + mapif_storage_saved(fd, aid, cid, true, type, stor.stor_id); return false; } diff --git a/src/char/int_storage.h b/src/char/int_storage.h index 9c391e4b45..80c88ff34e 100644 --- a/src/char/int_storage.h +++ b/src/char/int_storage.h @@ -11,6 +11,10 @@ void inter_storage_sql_final(void); void inter_storage_delete(uint32 account_id); void inter_guild_storage_delete(int guild_id); +bool inter_premiumStorage_exists(uint8 id); +int inter_premiumStorage_getMax(uint8 id); +const char *inter_premiumStorage_getTableName(uint8 id); + bool inter_storage_parse_frommap(int fd); bool guild_storage_tosql(int guild_id, struct s_storage *p); diff --git a/src/char/inter.c b/src/char/inter.c index e7b9ffc7dc..ecb43854b4 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -39,7 +39,7 @@ char char_server_id[32] = "ragnarok"; char char_server_pw[32] = ""; // Allow user to send empty password (bugreport:7787) char char_server_db[32] = "ragnarok"; char default_codepage[32] = ""; //Feature by irmin. - +struct Inter_Config interserv_config; unsigned int party_share_level = 10; /// Received packet Lengths from map-server @@ -52,7 +52,7 @@ int inter_recv_packet_length[] = { -1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] - 48,14,-1, 6, 0, 0, 0, 0, 0, 0,11,-1, 0, 0, 0, 0, // 3080- Pet System, Storage + 48,14,-1, 6, 0, 0, 0, 0, 0, 0,13,-1, 0, 0, 0, 0, // 3080- Pet System, Storage -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] }; @@ -773,6 +773,8 @@ static int inter_config_read(const char* cfgName) party_share_level = (unsigned int)atof(w2); else if(!strcmpi(w1,"log_inter")) charserv_config.log_inter = atoi(w2); + else if(!strcmpi(w1,"inter_server_conf")) + strcpy(interserv_config.cfgFile, w2); else if(!strcmpi(w1,"import")) inter_config_read(w2); } @@ -801,11 +803,86 @@ int inter_log(char* fmt, ...) return 0; } +/** + * Read inter config file + **/ +static void inter_config_readConf(void) { + int count = 0; + config_setting_t *config = NULL; + + if (conf_read_file(&interserv_config.cfg, interserv_config.cfgFile)) + return; + + // Read storages + config = config_lookup(&interserv_config.cfg, "storages"); + if (config && (count = config_setting_length(config))) { + int i; + for (i = 0; i < count; i++) { + int id, max_num; + const char *name, *tablename; + struct s_storage_table table; + config_setting_t *entry = config_setting_get_elem(config, i); + + if (!config_setting_lookup_int(entry, "id", &id)) { + ShowConfigWarning(entry, "inter_config_readConf: Cannot find storage \"id\" in member %d", i); + continue; + } + + if (!config_setting_lookup_string(entry, "name", &name)) { + ShowConfigWarning(entry, "inter_config_readConf: Cannot find storage \"name\" in member %d", i); + continue; + } + + if (!config_setting_lookup_string(entry, "table", &tablename)) { + ShowConfigWarning(entry, "inter_config_readConf: Cannot find storage \"table\" in member %d", i); + continue; + } + + if (!config_setting_lookup_int(entry, "max", &max_num)) + max_num = MAX_STORAGE; + else if (max_num > MAX_STORAGE) { + ShowConfigWarning(entry, "Storage \"%s\" has \"max\" %d, max is MAX_STORAGE (%d)!\n", name, max_num, MAX_STORAGE); + max_num = MAX_STORAGE; + } + + memset(&table, 0, sizeof(struct s_storage_table)); + + RECREATE(interserv_config.storages, struct s_storage_table, interserv_config.storage_count+1); + interserv_config.storages[interserv_config.storage_count].id = id; + safestrncpy(interserv_config.storages[interserv_config.storage_count].name, name, NAME_LENGTH); + safestrncpy(interserv_config.storages[interserv_config.storage_count].table, tablename, DB_NAME_LEN); + interserv_config.storages[interserv_config.storage_count].max_num = max_num; + interserv_config.storage_count++; + } + } + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' storage informations in '"CL_WHITE"%s"CL_RESET"'\n", interserv_config.storage_count, interserv_config.cfgFile); +} + +void inter_config_finalConf(void) { + + if (interserv_config.storages) + aFree(interserv_config.storages); + interserv_config.storages = NULL; + interserv_config.storage_count = 0; + + config_destroy(&interserv_config.cfg); +} + +static void inter_config_defaults(void) { + interserv_config.storage_count = 0; + interserv_config.storages = NULL; + + safestrncpy(interserv_config.cfgFile, "conf/inter_server.conf", sizeof(interserv_config.cfgFile)); +} + // initialize int inter_init_sql(const char *file) { //int i; + + inter_config_defaults(); inter_config_read(file); //DB connection initialized @@ -826,6 +903,7 @@ int inter_init_sql(const char *file) } wis_db = idb_alloc(DB_OPT_RELEASE_DATA); + inter_config_readConf(); inter_guild_sql_init(); inter_storage_sql_init(); inter_party_sql_init(); @@ -845,6 +923,7 @@ void inter_final(void) { wis_db->destroy(wis_db, NULL); + inter_config_finalConf(); inter_guild_sql_final(); inter_storage_sql_final(); inter_party_sql_final(); @@ -860,8 +939,28 @@ void inter_final(void) return; } +/** + * IZ 0x388c .W { .? }*? + * Sends storage information to map-server + * @param fd + **/ +void inter_Storage_sendInfo(int fd) { + int size = sizeof(struct s_storage_table), len = 4 + interserv_config.storage_count * size, i = 0; + // Send storage table information + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x388c; + WFIFOW(fd, 2) = len; + for (i = 0; i < interserv_config.storage_count; i++) { + if (!&interserv_config.storages[i] || !interserv_config.storages[i].name) + continue; + memcpy(WFIFOP(fd, 4 + size*i), &interserv_config.storages[i], size); + } + WFIFOSET(fd, len); +} + int inter_mapif_init(int fd) { + inter_Storage_sendInfo(fd); return 0; } diff --git a/src/char/inter.h b/src/char/inter.h index 16e7974120..d8de38e3df 100644 --- a/src/char/inter.h +++ b/src/char/inter.h @@ -4,8 +4,19 @@ #ifndef _INTER_SQL_H_ #define _INTER_SQL_H_ +#include "../common/conf.h" +#include "../common/mmo.h" #include "../common/sql.h" +struct Inter_Config { + char cfgFile[128]; ///< Inter-Config file + config_t cfg; ///< Config + struct s_storage_table *storages; ///< Storage name & table information + uint8 storage_count; ///< Number of available storage +}; + +extern struct Inter_Config interserv_config; + int inter_init_sql(const char *file); void inter_final(void); int inter_parse_frommap(int fd); diff --git a/src/common/mmo.h b/src/common/mmo.h index c5dd6f82f7..31bbe881ba 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -79,6 +79,7 @@ #define MAX_QUEST_DROPS 3 ///Max quest drops for a quest #define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh] #define MAX_ITEM_RDM_OPT 5 /// Max item random option [Napster] +#define DB_NAME_LEN 256 //max len of dbs // for produce #define MIN_ATTRIBUTE 0 @@ -303,13 +304,26 @@ enum storage_type { TABLE_GUILD_STORAGE, }; +enum e_storage_mode { + STOR_MODE_NONE = 0x0, + STOR_MODE_GET = 0x1, + STOR_MODE_PUT = 0x2, + STOR_MODE_ALL = 0x3, +}; + struct s_storage { bool dirty; ///< Dirty status, data needs to be saved bool status; ///< Current status of storage (opened or closed) - int amount; ///< Amount of items in storage + uint16 amount; ///< Amount of items in storage bool lock; ///< If locked, can't use storage when item bound retrieval uint32 id; ///< Account ID / Character ID / Guild ID (owner of storage) enum storage_type type; ///< Type of storage (inventory, cart, storage, guild storage) + uint16 max_amount; + uint8 stor_id; ///< Storage ID + struct { + unsigned get : 1; + unsigned put : 1; + } state; union { // Max for inventory, storage, cart, and guild storage are 1637 each without changing this struct and struct item [2014/10/27] struct item items_inventory[MAX_INVENTORY]; struct item items_storage[MAX_STORAGE]; @@ -318,6 +332,13 @@ struct s_storage { } u; }; +struct s_storage_table { + char name[NAME_LENGTH]; + char table[DB_NAME_LEN]; + uint16 max_num; + uint8 id; +}; + struct s_pet { uint32 account_id; uint32 char_id; diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 5f73e01854..19fac25783 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -922,6 +922,11 @@ ACMD_FUNC(guildstorage) return -1; } + if (sd->state.storage_flag == 3) { + clif_displaymessage(fd, msg_txt(sd,250)); + return -1; + } + storage_guild_storageopen(sd); clif_displaymessage(fd, msg_txt(sd,920)); // Guild storage opened. return 0; @@ -5490,7 +5495,7 @@ ACMD_FUNC(storeall) if (sd->inventory.u.items_inventory[i].amount) { if(sd->inventory.u.items_inventory[i].equip != 0) pc_unequipitem(sd, i, 3); - storage_storageadd(sd, i, sd->inventory.u.items_inventory[i].amount); + storage_storageadd(sd, &sd->storage, i, sd->inventory.u.items_inventory[i].amount); } } storage_storageclose(sd); @@ -5508,10 +5513,14 @@ ACMD_FUNC(clearstorage) clif_displaymessage(fd, msg_txt(sd,250)); return -1; } + if (sd->state.storage_flag == 3) { + clif_displaymessage(fd, msg_txt(sd,250)); + return -1; + } j = sd->storage.amount; for (i = 0; i < j; ++i) { - storage_delitem(sd, i, sd->storage.u.items_storage[i].amount); + storage_delitem(sd, &sd->storage, i, sd->storage.u.items_storage[i].amount); } sd->state.storage_flag = 1; storage_storageclose(sd); @@ -5544,6 +5553,11 @@ ACMD_FUNC(cleargstorage) return -1; } + if (sd->state.storage_flag == 3) { + clif_displaymessage(fd, msg_txt(sd,250)); + return -1; + } + gstorage = guild2storage2(sd->status.guild_id); if (gstorage == NULL) { // Doesn't have opened @gstorage yet, so we skip the deletion since *shouldn't* have any item there. return -1; @@ -8606,7 +8620,7 @@ ACMD_FUNC(itemlist) if( strcmp(parent_cmd, "storagelist") == 0 ) { location = "storage"; items = sd->storage.u.items_storage; - size = sd->storage_size; + size = sd->storage.max_amount; } else if( strcmp(parent_cmd, "cartlist") == 0 ) { location = "cart"; items = sd->cart.u.items_cart; diff --git a/src/map/chrif.c b/src/map/chrif.c index 65bf2f37f8..1563fdbc78 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -300,13 +300,15 @@ int chrif_save(struct map_session_data *sd, int flag) { chrif_bsdata_save(sd, (flag && (flag != 3))); if (sd->state.storage_flag == 1) - intif_storage_save(sd,TABLE_STORAGE); - intif_storage_save(sd,TABLE_INVENTORY); - intif_storage_save(sd,TABLE_CART); + intif_storage_save(sd,&sd->storage); + intif_storage_save(sd,&sd->inventory); + intif_storage_save(sd,&sd->cart); //For data sync if (sd->state.storage_flag == 2) storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag); + if (&sd->premiumStorage && sd->premiumStorage.dirty) + intif_storage_save(sd, &sd->premiumStorage); if (flag) sd->state.storage_flag = 0; //Force close it. @@ -1602,10 +1604,10 @@ void chrif_parse_ack_vipActive(int fd) { sd->vip.enabled = 1; sd->vip.time = vip_time; // Increase storage size for VIP. - sd->storage_size = battle_config.vip_storage_increase + MIN_STORAGE; - if (sd->storage_size > MAX_STORAGE) { + sd->storage.max_amount = battle_config.vip_storage_increase + MIN_STORAGE; + if (sd->storage.max_amount > MAX_STORAGE) { ShowError("intif_parse_ack_vipActive: Storage size for player %s (%d:%d) is larger than MAX_STORAGE. Storage size has been set to MAX_STORAGE.\n", sd->status.name, sd->status.account_id, sd->status.char_id); - sd->storage_size = MAX_STORAGE; + sd->storage.max_amount = MAX_STORAGE; } // Magic Stone requirement avoidance for VIP. if (battle_config.vip_gemstone) @@ -1613,7 +1615,7 @@ void chrif_parse_ack_vipActive(int fd) { } else if (sd->vip.enabled) { sd->vip.enabled = 0; sd->vip.time = 0; - sd->storage_size = MIN_STORAGE; + sd->storage.max_amount = MIN_STORAGE; sd->special_state.no_gemstone = 0; clif_displaymessage(sd->fd,msg_txt(sd,438)); } @@ -1998,6 +2000,12 @@ void do_init_chrif(void) { exit(EXIT_FAILURE); } + if (sizeof(struct s_storage) > 0xFFFF){ + ShowError("s_storage size = %d is too big to be transmitted. (must be below 0xFFFF) \n", + sizeof(struct s_storage)); + exit(EXIT_FAILURE); + } + if((sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT) > 0xFFFF){ ShowError("bonus_script_data size = %d is too big, please reduce MAX_PC_BONUS_SCRIPT (%d) size. (must be below 0xFFFF).\n", (sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT), MAX_PC_BONUS_SCRIPT); diff --git a/src/map/clif.c b/src/map/clif.c index e495d84c4b..b04995719f 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2813,7 +2813,7 @@ void clif_equiplist(struct map_session_data *sd) } } -void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length) +void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length, const char *storename) { static const int client_buf = 0x5000; // Max buffer to send struct item_data *id; @@ -2883,7 +2883,7 @@ void clif_storagelist(struct map_session_data* sd, struct item* items, int items WFIFOW(sd->fd,0)=cmd; WFIFOW(sd->fd,2)=sidx+nn*s; #if PACKETVER >= 20120925 - memset((char*)WFIFOP(sd->fd,4),0,24); //storename + safestrncpy((char*)WFIFOP(sd->fd,4), storename, NAME_LENGTH); //storename #endif memcpy(WFIFOP(sd->fd,sidx),buf + sidx + i*s,nn*s); WFIFOSET(sd->fd,WFIFOW(sd->fd,2)); @@ -2895,12 +2895,23 @@ void clif_storagelist(struct map_session_data* sd, struct item* items, int items WFIFOW(sd->fd,0)=cmde; WFIFOW(sd->fd,2)=sidxe+nn*se; #if PACKETVER >= 20120925 - memset((char*)WFIFOP(sd->fd,4),0,24); //storename + safestrncpy((char*)WFIFOP(sd->fd,4), storename, NAME_LENGTH); //storename #endif memcpy(WFIFOP(sd->fd,sidxe),bufe + sidxe + i*se,nn*se); WFIFOSET(sd->fd,WFIFOW(sd->fd,2)); } + // Empty storage + if (n == 0 && ne == 0) { + WFIFOHEAD(sd->fd, 4+NAME_LENGTH); + WFIFOW(sd->fd,0) = cmd; + WFIFOW(sd->fd,2) = 4+NAME_LENGTH; +#if PACKETVER >= 20120925 + safestrncpy((char*)WFIFOP(sd->fd,4), storename, NAME_LENGTH); //storename +#endif + WFIFOSET(sd->fd,WFIFOW(sd->fd,2)); + } + if( buf ) aFree(buf); if( bufe ) aFree(bufe); } @@ -9155,7 +9166,7 @@ void clif_refresh_storagewindow(struct map_session_data *sd) { // Notify the client that the storage is open if( 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)); + clif_storagelist(sd, sd->storage.u.items_storage, ARRAYLENGTH(sd->storage.u.items_storage), storage_getName(0)); clif_updatestorageamount(sd, sd->storage.amount, MAX_STORAGE); } // Notify the client that the gstorage is open otherwise it will @@ -9167,7 +9178,7 @@ void clif_refresh_storagewindow(struct map_session_data *sd) { intif_request_guild_storage(sd->status.account_id, sd->status.guild_id); else { storage_sortitem(gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild)); - clif_storagelist(sd, 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, MAX_GUILD_STORAGE); } } @@ -12504,10 +12515,12 @@ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) return; if (sd->state.storage_flag == 1) - storage_storageadd(sd, item_index, item_amount); + storage_storageadd(sd, &sd->storage, item_index, item_amount); else if (sd->state.storage_flag == 2) storage_guild_storageadd(sd, item_index, item_amount); + else if (sd->state.storage_flag == 3) + storage_storageadd(sd, &sd->premiumStorage, item_index, item_amount); } @@ -12524,9 +12537,11 @@ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) item_amount = RFIFOL(fd,info->pos[1]); if (sd->state.storage_flag == 1) - storage_storageget(sd, item_index, item_amount); + storage_storageget(sd, &sd->storage, item_index, item_amount); else if(sd->state.storage_flag == 2) storage_guild_storageget(sd, item_index, item_amount); + else if(sd->state.storage_flag == 3) + storage_storageget(sd, &sd->premiumStorage, item_index, item_amount); } @@ -12544,9 +12559,11 @@ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd){ return; if (sd->state.storage_flag == 1) - storage_storageaddfromcart(sd, idx, amount); + storage_storageaddfromcart(sd, &sd->storage, idx, amount); else if (sd->state.storage_flag == 2) storage_guild_storageaddfromcart(sd, idx, amount); + else if (sd->state.storage_flag == 3) + storage_storageaddfromcart(sd, &sd->premiumStorage, idx, amount); } @@ -12563,10 +12580,12 @@ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd){ return; if (sd->state.storage_flag == 1) - storage_storagegettocart(sd, idx, amount); + storage_storagegettocart(sd, &sd->storage, idx, amount); else if (sd->state.storage_flag == 2) storage_guild_storagegettocart(sd, idx, amount); + else if (sd->state.storage_flag == 3) + storage_storagegettocart(sd, &sd->premiumStorage, idx, amount); } @@ -12579,6 +12598,8 @@ void clif_parse_CloseKafra(int fd, struct map_session_data *sd) else if( sd->state.storage_flag == 2 ) storage_guild_storageclose(sd); + else if( sd->state.storage_flag == 3 ) + storage_premiumStorage_close(sd); } diff --git a/src/map/clif.h b/src/map/clif.h index cb61868904..731e94625a 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -616,7 +616,7 @@ void clif_tradecompleted(struct map_session_data* sd, int fail); void clif_tradeundo(struct map_session_data* sd); // storage -void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length); +void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length, const char *storename); void clif_updatestorageamount(struct map_session_data* sd, int amount, int max_amount); void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount); void clif_storageitemremoved(struct map_session_data* sd, int index, int amount); diff --git a/src/map/intif.c b/src/map/intif.c index 446e7b150a..ea059cea04 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -35,7 +35,7 @@ static const int packet_len_table[] = { -1,-1, 7, 7, 7,11, 8,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari] -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] - 12,-1, 7, 3, 0, 0, 0, 0, 0, 0,-1, 8, 0, 0, 0, 0, //0x3880 Pet System, Storages + 12,-1, 7, 3, 0, 0, 0, 0, 0, 0,-1, 9, -1, 0, 0, 0, //0x3880 Pet System, Storages -1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] }; @@ -3146,7 +3146,7 @@ static bool intif_parse_StorageReceived(int fd) char type = RFIFOB(fd,4); uint32 account_id = RFIFOL(fd, 5); struct map_session_data *sd = map_id2sd(account_id); - struct s_storage *stor; //storage + struct s_storage *stor, *p; //storage size_t sz_stor = sizeof(struct s_storage); if (!sd) { @@ -3159,22 +3159,31 @@ static bool intif_parse_StorageReceived(int fd) return false; } + p = (struct s_storage *)RFIFOP(fd,10); + switch (type) { case TABLE_INVENTORY: stor = &sd->inventory; break; - case TABLE_STORAGE: stor = &sd->storage; break; + case TABLE_STORAGE: + if (p->stor_id == 0) + stor = &sd->storage; + else + stor = &sd->premiumStorage; + break; case TABLE_CART: stor = &sd->cart; break; default: return false; } - if (stor->status) { // Already open.. lets ignore this update - ShowWarning("intif_parse_StorageReceived: storage received for a client already open (User %d:%d)\n", sd->status.account_id, sd->status.char_id); - return false; - } - if (stor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! - ShowWarning("intif_parse_StorageReceived: received storage for an already modified non-saved storage! (User %d:%d)\n", sd->status.account_id, sd->status.char_id); - return false; + if (stor->stor_id == p->stor_id) { + if (stor->status) { // Already open.. lets ignore this update + ShowWarning("intif_parse_StorageReceived: storage received for a client already open (User %d:%d)\n", sd->status.account_id, sd->status.char_id); + return false; + } + if (stor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! + ShowWarning("intif_parse_StorageReceived: received storage for an already modified non-saved storage! (User %d:%d)\n", sd->status.account_id, sd->status.char_id); + return false; + } } if (RFIFOW(fd,2)-10 != sz_stor) { ShowError("intif_parse_StorageReceived: data size error %d %d\n",RFIFOW(fd,2)-10 , sz_stor); @@ -3182,7 +3191,7 @@ static bool intif_parse_StorageReceived(int fd) return false; } - memcpy(stor, RFIFOP(fd,10), sz_stor); //copy the items data to correct destination + memcpy(stor, p, sz_stor); //copy the items data to correct destination switch (type) { case TABLE_INVENTORY: { @@ -3224,7 +3233,15 @@ static bool intif_parse_StorageReceived(int fd) break; case TABLE_STORAGE: - pc_check_available_item(sd, ITMCHK_STORAGE); + if (stor->stor_id) + storage_premiumStorage_open(sd); + else { +#ifdef VIP_ENABLE + if (!pc_isvip(sd)) + stor->max_amount = MIN_STORAGE; +#endif + pc_check_available_item(sd, ITMCHK_STORAGE); + } break; } return true; @@ -3232,7 +3249,7 @@ static bool intif_parse_StorageReceived(int fd) /** * Save inventory/cart/storage data for a player - * IZ 0x388b .L .B .B + * IZ 0x388b .L .B .B .B * @param fd */ static void intif_parse_StorageSaved(int fd) @@ -3244,6 +3261,8 @@ static void intif_parse_StorageSaved(int fd) break; case TABLE_STORAGE: //storage //ShowInfo("Storage has been saved (AID: %d).\n", RFIFOL(fd, 2)); + if (RFIFOB(fd, 8)) + storage_premiumStorage_saved(map_id2sd(RFIFOL(fd, 2))); break; case TABLE_CART: // cart //ShowInfo("Cart has been saved (AID: %d).\n", RFIFOL(fd, 2)); @@ -3251,7 +3270,7 @@ static void intif_parse_StorageSaved(int fd) struct map_session_data *sd = map_id2sd(RFIFOL(fd, 2)); if( sd && sd->state.prevend ){ - intif_storage_request(sd,TABLE_CART); + intif_storage_request(sd,TABLE_CART,0,STOR_MODE_ALL); } } break; @@ -3262,24 +3281,54 @@ static void intif_parse_StorageSaved(int fd) ShowError("Failed to save inventory/cart/storage data (AID: %d, type: %d).\n", RFIFOL(fd, 2), RFIFOB(fd, 7)); } +/** + * IZ 0x388c .W { .? }*? + * Receive storage information + **/ +void intif_parse_StorageInfo_recv(int fd) { + int i, size = sizeof(struct s_storage_table), count = (RFIFOW(fd, 2) - 4) / size; + + storage_count = 0; + if (storage_db) + aFree(storage_db); + storage_db = NULL; + + for (i = 0; i < count; i++) { + char name[NAME_LENGTH+1]; + safestrncpy(name, (char *)RFIFOP(fd, 5 + size * i), NAME_LENGTH); + if (!name || name[0] == '\0') + continue; + RECREATE(storage_db, struct s_storage_table, storage_count+1); + memcpy(&storage_db[storage_count], RFIFOP(fd, 4 + size * i), size); + storage_count++; + } + + if (battle_config.etc_log) + ShowInfo("Received '"CL_WHITE"%d"CL_RESET"' storage info from inter-server.\n", storage_count); +} + /** * Request inventory/cart/storage data for a player - * ZI 0x308a .B .L .L + * ZI 0x308a .B .L .L .B * @param sd: Player data * @param type: Storage type + * @param stor_id: Storage ID + * @param mode: Storage mode * @return false - error, true - message sent */ -bool intif_storage_request(struct map_session_data *sd, enum storage_type type) +bool intif_storage_request(struct map_session_data *sd, enum storage_type type, uint8 stor_id, uint8 mode) { if (CheckForCharServer()) return false; - WFIFOHEAD(inter_fd, 11); + WFIFOHEAD(inter_fd, 13); WFIFOW(inter_fd, 0) = 0x308a; WFIFOB(inter_fd, 2) = type; WFIFOL(inter_fd, 3) = sd->status.account_id; WFIFOL(inter_fd, 7) = sd->status.char_id; - WFIFOSET(inter_fd, 11); + WFIFOB(inter_fd, 11) = stor_id; + WFIFOB(inter_fd, 12) = mode; + WFIFOSET(inter_fd, 13); return true; } @@ -3287,37 +3336,23 @@ bool intif_storage_request(struct map_session_data *sd, enum storage_type type) * Request to save inventory/cart/storage data from player * ZI 0x308b .W .B .L .L .?B * @param sd: Player data - * @param type: Storage type + * @param stor: Storage data * @ return false - error, true - message sent */ -bool intif_storage_save(struct map_session_data *sd, enum storage_type type) +bool intif_storage_save(struct map_session_data *sd, struct s_storage *stor) { int stor_size = sizeof(struct s_storage); - struct s_storage *stor; nullpo_retr(false, sd); + nullpo_retr(false, stor); if (CheckForCharServer()) return false; - switch(type) { - case TABLE_INVENTORY: - stor = &sd->inventory; - break; - case TABLE_STORAGE: - stor = &sd->storage; - break; - case TABLE_CART: - stor = &sd->cart; - break; - default: - return false; - } - WFIFOHEAD(inter_fd, stor_size+13); WFIFOW(inter_fd, 0) = 0x308b; WFIFOW(inter_fd, 2) = stor_size+13; - WFIFOB(inter_fd, 4) = type; + WFIFOB(inter_fd, 4) = stor->type; WFIFOL(inter_fd, 5) = sd->status.account_id; WFIFOL(inter_fd, 9) = sd->status.char_id; memcpy(WFIFOP(inter_fd, 13), stor, stor_size); @@ -3440,6 +3475,7 @@ int intif_parse(int fd) // Storage case 0x388a: intif_parse_StorageReceived(fd); break; case 0x388b: intif_parse_StorageSaved(fd); break; + case 0x388c: intif_parse_StorageInfo_recv(fd); break; // Homunculus System case 0x3890: intif_parse_CreateHomunculus(fd); break; diff --git a/src/map/intif.h b/src/map/intif.h index 79c8d4d9a0..e5d6b9c45c 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -111,8 +111,8 @@ int intif_elemental_save(struct s_elemental *ele); int intif_request_accinfo(int u_fd, int aid, int group_lv, char* query, char type); // STORAGE -bool intif_storage_request(struct map_session_data *sd, enum storage_type type); -bool intif_storage_save(struct map_session_data *sd, enum storage_type type); +bool intif_storage_request(struct map_session_data *sd, enum storage_type type, uint8 stor_id, uint8 mode); +bool intif_storage_save(struct map_session_data *sd, struct s_storage *stor); int CheckForCharServer(void); diff --git a/src/map/pc.c b/src/map/pc.c index 56a64cb300..5c5a774412 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1151,6 +1151,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ memset(&sd->inventory, 0, sizeof(struct s_storage)); memset(&sd->cart, 0, sizeof(struct s_storage)); memset(&sd->storage, 0, sizeof(struct s_storage)); + memset(&sd->premiumStorage, 0, sizeof(struct s_storage)); memset(&sd->equip_index, -1, sizeof(sd->equip_index)); if( sd->status.option&OPTION_INVISIBLE && !pc_can_use_command( sd, "hide", COMMAND_ATCOMMAND ) ){ @@ -1399,11 +1400,9 @@ void pc_reg_received(struct map_session_data *sd) return; sd->state.active = 1; - intif_storage_request(sd,TABLE_INVENTORY); // Request inventory data - intif_storage_request(sd,TABLE_CART); // Request cart data - intif_storage_request(sd,TABLE_STORAGE); // Request storage data - - sd->storage_size = MIN_STORAGE; //default to min + intif_storage_request(sd,TABLE_STORAGE, 0, STOR_MODE_ALL); // Request storage data + intif_storage_request(sd,TABLE_CART, 0, STOR_MODE_ALL); // Request cart data + intif_storage_request(sd,TABLE_INVENTORY, 0, STOR_MODE_ALL); // Request inventory data if (sd->status.party_id) party_member_joined(sd); @@ -9970,7 +9969,7 @@ void pc_check_available_item(struct map_session_data *sd, uint8 type) } if (battle_config.item_check&ITMCHK_STORAGE || type&ITMCHK_STORAGE) { // Check for invalid(ated) items in storage. - for(i = 0; i < sd->storage_size; i++) { + for(i = 0; i < sd->storage.max_amount; i++) { nameid = sd->storage.u.items_storage[i].nameid; if (!nameid) @@ -9979,7 +9978,7 @@ void pc_check_available_item(struct map_session_data *sd, uint8 type) sprintf(output, msg_txt(sd, 711), nameid); // Item %hu has been removed from your storage. clif_displaymessage(sd->fd, output); ShowWarning("Removed invalid/disabled item (ID: %hu, amount: %d) from storage (char_id: %d).\n", nameid, sd->storage.u.items_storage[i].amount, sd->status.char_id); - storage_delitem(sd, i, sd->storage.u.items_storage[i].amount); + storage_delitem(sd, &sd->storage, i, sd->storage.u.items_storage[i].amount); continue; } if (!sd->storage.u.items_storage[i].unique_id && !itemdb_isstackable(nameid)) diff --git a/src/map/pc.h b/src/map/pc.h index 5d5e78e19f..0f3ebc1093 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -206,7 +206,7 @@ struct map_session_data { unsigned int arrow_atk : 1; unsigned int gangsterparadise : 1; unsigned int rest : 1; - unsigned int storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex] + unsigned int storage_flag : 3; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex], 3: Premium Storage unsigned int snovice_dead_flag : 1; //Explosion spirits on death: 0 off, 1 used. unsigned int abra_flag : 2; // Abracadabra bugfix by Aru unsigned int autocast : 1; // Autospell flag [Inkfish] @@ -275,7 +275,7 @@ struct map_session_data { struct mmo_charstatus status; // Item Storages - struct s_storage storage; + struct s_storage storage, premiumStorage; struct s_storage inventory; struct s_storage cart; @@ -654,7 +654,6 @@ struct map_session_data { int c_marker[MAX_SKILL_CRIMSON_MARKER]; /// Store target that marked by Crimson Marker [Cydh] bool flicker; /// Check RL_FLICKER usage status [Cydh] - int storage_size; /// Holds player storage size (VIP system). #ifdef VIP_ENABLE struct vip_info vip; #endif diff --git a/src/map/script.c b/src/map/script.c index 9ba4e0eb98..8050e6d2fe 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -7382,7 +7382,7 @@ static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* am pc_cart_delitem(sd,idx,delamount,0,LOG_TYPE_SCRIPT); break; case TABLE_STORAGE: - storage_delitem(sd,idx,delamount); + storage_delitem(sd,&sd->storage,idx,delamount); log_pick_pc(sd,LOG_TYPE_SCRIPT,-delamount,itm); break; case TABLE_GUILD_STORAGE: @@ -22012,7 +22012,28 @@ BUILDIN_FUNC(getguildalliance) script_pushint(st, 2); else script_pushint(st, 1); + return SCRIPT_CMD_SUCCESS; +} +/* + * openstorage2 ,{,} + * mode @see enum e_storage_mode + **/ +BUILDIN_FUNC(openstorage2) { + int stor_id = script_getnum(st, 2); + TBL_PC *sd = NULL; + + if (!script_accid2sd(4, sd)) { + script_pushint(st, 0); + return SCRIPT_CMD_FAILURE; + } + + if (!storage_exists(stor_id)) { + ShowError("buildin_openstorage2: Invalid storage_id '%d'!\n", stor_id); + return SCRIPT_CMD_FAILURE; + } + + script_pushint(st, storage_premiumStorage_load(sd, stor_id, script_getnum(st, 3))); return SCRIPT_CMD_SUCCESS; } @@ -22607,6 +22628,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(setrandomoption,"iiiii?"), BUILDIN_DEF(needed_status_point,"ii?"), BUILDIN_DEF(jobcanentermap,"s?"), + BUILDIN_DEF(openstorage2,"ii?"), // WoE TE BUILDIN_DEF(agitstart3,""), diff --git a/src/map/script_constants.h b/src/map/script_constants.h index 074a90d034..524d6daf0b 100644 --- a/src/map/script_constants.h +++ b/src/map/script_constants.h @@ -3170,6 +3170,10 @@ export_constant(CARD0_CREATE); export_constant(CARD0_PET); + export_constant(STOR_MODE_NONE); + export_constant(STOR_MODE_GET); + export_constant(STOR_MODE_PUT); + #undef export_constant #endif /* _SCRIPT_CONSTANTS_H_ */ diff --git a/src/map/skill.c b/src/map/skill.c index 2cc66b5f4f..01abe1a11b 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -7319,7 +7319,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui sd->vend_skill_lv = skill_lv; ARR_FIND(0, MAX_CART, i, sd->cart.u.items_cart[i].nameid && sd->cart.u.items_cart[i].id == 0); if (i < MAX_CART) - intif_storage_save(sd, TABLE_CART); + intif_storage_save(sd, &sd->cart); else clif_openvendingreq(sd,2+skill_lv); } diff --git a/src/map/storage.c b/src/map/storage.c index 92cadc8777..5f5bc4f1eb 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -20,6 +20,41 @@ static DBMap* guild_storage_db; ///Databases of guild_storage : int guild_id -> struct guild_storage* +struct s_storage_table *storage_db; +int storage_count; + +/** + * Get storage name + * @param id Storage ID + * @return Storage name or "Storage" if not found + * @author [Cydh] + **/ +const char *storage_getName(uint8 id) { + if (storage_db && storage_count) { + int i; + for (i = 0; i < storage_count; i++) { + if (&storage_db[i] && storage_db[i].id == id && storage_db[i].name && storage_db[i].name[0] != '\0') + return storage_db[i].name; + } + } + return "Storage"; +} + +/** + * Check if sotrage ID is valid + * @param id Storage ID + * @return True:Valid, False:Invalid + **/ +bool storage_exists(uint8 id) { + if (storage_db && storage_count) { + int i; + for (i = 0; i < storage_count; i++) { + if (storage_db[i].id == id) + return true; + } + } + return false; +} /** * Storage item comparator (for qsort) @@ -64,6 +99,8 @@ void storage_sortitem(struct item* items, unsigned int size) void do_init_storage(void) { guild_storage_db = idb_alloc(DB_OPT_RELEASE_DATA); + storage_db = NULL; + storage_count = 0; } /** @@ -74,6 +111,10 @@ void do_init_storage(void) void do_final_storage(void) { guild_storage_db->destroy(guild_storage_db,NULL); + if (storage_db) + aFree(storage_db); + storage_db = NULL; + storage_count = 0; } /** @@ -119,9 +160,9 @@ int storage_storageopen(struct map_session_data *sd) } 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)); - clif_updatestorageamount(sd, sd->storage.amount, sd->storage_size); + storage_sortitem(sd->storage.u.items_storage, sd->storage.max_amount); + clif_storagelist(sd, sd->storage.u.items_storage, sd->storage.max_amount, storage_getName(0)); + clif_updatestorageamount(sd, sd->storage.amount, sd->storage.max_amount); return 0; } @@ -153,44 +194,98 @@ int compare_item(struct item *a, struct item *b) 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 (stor->amount >= stor->max_amount) + return STORAGE_ADD_NOROOM; // storage full + + 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 (!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 */ -static int storage_additem(struct map_session_data* sd, struct item* item_data, int amount) +static int storage_additem(struct map_session_data* sd, struct s_storage *stor, struct item *it, int amount) { - struct s_storage* stor = &sd->storage; struct item_data *data; int i; - if( item_data->nameid == 0 || amount <= 0 ) + if( it->nameid == 0 || amount <= 0 ) return 1; - data = itemdb_search(item_data->nameid); + data = itemdb_search(it->nameid); if( data->stack.storage && amount > data->stack.amount ) // item stack limitation return 1; - if( !itemdb_canstore(item_data, pc_get_group_level(sd)) ) { // Check if item is storable. [Skotlex] + 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( (item_data->bound > BOUND_ACCOUNT) && !pc_can_give_bounded_items(sd) ) { + 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 < sd->storage_size; i++ ) { - if( compare_item(&stor->u.items_storage[i], item_data) ) { // existing items found, stack them + 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 1; stor->u.items_storage[i].amount += amount; + stor->dirty = true; clif_storageitemadded(sd,&stor->u.items_storage[i],i,amount); return 0; @@ -199,16 +294,17 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, } // find free slot - ARR_FIND( 0, sd->storage_size, i, stor->u.items_storage[i].nameid == 0 ); - if( i >= sd->storage_size ) + ARR_FIND( 0, stor->max_amount, i, stor->u.items_storage[i].nameid == 0 ); + if( i >= stor->max_amount ) return 1; // add item to slot - memcpy(&stor->u.items_storage[i],item_data,sizeof(stor->u.items_storage[0])); + 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, sd->storage_size); + clif_updatestorageamount(sd, stor->amount, stor->max_amount); return 0; } @@ -220,22 +316,23 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, * @param amount :number of item to remove * @return 0:sucess, 1:fail */ -int storage_delitem(struct map_session_data* sd, int n, int amount) +int storage_delitem(struct map_session_data* sd, struct s_storage *stor, int index, int amount) { - if( sd->storage.u.items_storage[n].nameid == 0 || sd->storage.u.items_storage[n].amount < amount ) + if( stor->u.items_storage[index].nameid == 0 || stor->u.items_storage[index].amount < amount ) return 1; - sd->storage.u.items_storage[n].amount -= amount; + stor->u.items_storage[index].amount -= amount; + stor->dirty = true; - if( sd->storage.u.items_storage[n].amount == 0 ) { - memset(&sd->storage.u.items_storage[n],0,sizeof(sd->storage.u.items_storage[0])); - sd->storage.amount--; - if( sd->state.storage_flag == 1 ) - clif_updatestorageamount(sd, sd->storage.amount, sd->storage_size); + 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, sd->storage.max_amount); } - if( sd->state.storage_flag == 1 ) - clif_storageitemremoved(sd,n,amount); + if( sd->state.storage_flag == 1 || sd->state.storage_flag == 3 ) + clif_storageitemremoved(sd,index,amount); return 0; } @@ -243,32 +340,27 @@ int storage_delitem(struct map_session_data* sd, int n, int amount) /** * 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(struct map_session_data* sd, int index, int amount) +void storage_storageadd(struct map_session_data* sd, struct s_storage *stor, int index, int amount) { + enum e_storage_add result; + nullpo_retv(sd); - if( sd->storage.amount > sd->storage_size ) - return; // storage full - - if( index < 0 || index >= MAX_INVENTORY ) + result = storage_canAddItem(stor, index, sd->inventory.u.items_inventory, amount, MAX_INVENTORY); + if (result == STORAGE_ADD_INVALID) return; - - if( sd->inventory.u.items_inventory[index].nameid <= 0 ) - return; // No item on that spot - - if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount ) - return; - - if( storage_additem(sd,&sd->inventory.u.items_inventory[index],amount) == 0 ) + else if (result == STORAGE_ADD_OK && storage_additem(sd,stor,&sd->inventory.u.items_inventory[index],amount) == 0) { pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE); - else { - clif_storageitemremoved(sd,index,0); - clif_dropitem(sd,index,0); + return; } + + clif_storageitemremoved(sd,index,0); + clif_dropitem(sd,index,0); } /** @@ -278,21 +370,19 @@ void storage_storageadd(struct map_session_data* sd, int index, int amount) * @param amount : number of item to take * @return 0:fail, 1:success */ -void storage_storageget(struct map_session_data* sd, int index, int amount) +void storage_storageget(struct map_session_data *sd, struct s_storage *stor, int index, int amount) { unsigned char flag = 0; + enum e_storage_add result; - if( index < 0 || index >= sd->storage_size ) + nullpo_retv(sd); + + result = storage_canGetItem(stor, index, amount); + if (result != STORAGE_ADD_OK) return; - if( sd->storage.u.items_storage[index].nameid <= 0 ) - return; //Nothing there - - if( amount < 1 || amount > sd->storage.u.items_storage[index].amount ) - return; - - if( (flag = pc_additem(sd,&sd->storage.u.items_storage[index],amount,LOG_TYPE_STORAGE)) == 0 ) - storage_delitem(sd,index,amount); + 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); @@ -302,58 +392,49 @@ void storage_storageget(struct map_session_data* sd, int index, int amount) /** * 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(struct map_session_data* sd, int index, int amount) +void storage_storageaddfromcart(struct map_session_data *sd, struct s_storage *stor, int index, int amount) { + enum e_storage_add result; nullpo_retv(sd); - if( sd->storage.amount > sd->storage_size ) - return; // storage full / storage closed - - if( index < 0 || index >= MAX_CART ) + result = storage_canAddItem(stor, index, sd->cart.u.items_inventory, amount, MAX_INVENTORY); + if (result == STORAGE_ADD_INVALID) return; - - if( sd->cart.u.items_cart[index].nameid <= 0 ) - return; //No item there. - - if( amount < 1 || amount > sd->cart.u.items_cart[index].amount ) - return; - - if( storage_additem(sd,&sd->cart.u.items_cart[index],amount) == 0 ) + else if (result == STORAGE_ADD_OK && storage_additem(sd,stor,&sd->cart.u.items_cart[index],amount) == 0) { pc_cart_delitem(sd,index,amount,0,LOG_TYPE_STORAGE); - else { - clif_storageitemremoved(sd,index,0); - clif_dropitem(sd,index,0); + return; } + + 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(struct map_session_data* sd, int index, int amount) +void storage_storagegettocart(struct map_session_data* sd, struct s_storage *stor, int index, int amount) { - unsigned char flag; + unsigned char flag = 0; + enum e_storage_add result; nullpo_retv(sd); - if( index < 0 || index >= sd->storage_size ) + result = storage_canGetItem(stor, index, amount); + if (result != STORAGE_ADD_OK) return; - if( sd->storage.u.items_storage[index].nameid <= 0 ) - return; //Nothing there. - - if( amount < 1 || amount > sd->storage.u.items_storage[index].amount ) - return; - - if( (flag = pc_cart_additem(sd,&sd->storage.u.items_storage[index],amount,LOG_TYPE_STORAGE)) == 0 ) - storage_delitem(sd,index,amount); + 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); clif_cart_additem_ack(sd,(flag==1)?ADDITEM_TO_CART_FAIL_WEIGHT:ADDITEM_TO_CART_FAIL_COUNT); @@ -487,7 +568,7 @@ char storage_guild_storageopen(struct map_session_data* sd) 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)); + clif_storagelist(sd, gstor->u.items_guild, ARRAYLENGTH(gstor->u.items_guild), "Guild Storage"); clif_updatestorageamount(sd, gstor->amount, MAX_GUILD_STORAGE); return 0; @@ -871,3 +952,121 @@ void storage_guild_storage_quit(struct map_session_data* sd, int flag) sd->state.storage_flag = 0; stor->status = false; } + +/** + * Open premium storage + * @param sd Player + **/ +void storage_premiumStorage_open(struct map_session_data *sd) { + nullpo_retv(sd); + + if (!&sd->premiumStorage) + return; + + sd->state.storage_flag = 3; + storage_sortitem(sd->premiumStorage.u.items_storage, sd->premiumStorage.max_amount); + clif_storagelist(sd, sd->premiumStorage.u.items_storage, sd->premiumStorage.max_amount, 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(struct 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 || 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(struct map_session_data *sd) { + nullpo_retv(sd); + + if (!&sd->premiumStorage) + return; + + intif_storage_save(sd, &sd->premiumStorage); +} + +/** + * Ack of secondary premium has been saved + * @param sd Player who has the storage + **/ +void storage_premiumStorage_saved(struct map_session_data *sd) { + if (!sd) + return; + + if (&sd->premiumStorage) { + sd->premiumStorage.dirty = 0; + } + if (sd->state.storage_flag == 3) { + sd->state.storage_flag = 0; + clif_storageclose(sd); + } +} + +/** + * Request to close premium storage + * @param sd Player who has the storage + * @author [Cydh] + **/ +void storage_premiumStorage_close(struct map_session_data *sd) { + nullpo_retv(sd); + + if (!&sd->premiumStorage) + return; + + if (sd->premiumStorage.dirty) { + intif_storage_save(sd, &sd->premiumStorage); + if (sd->state.storage_flag == 3) { + sd->state.storage_flag = 0; + clif_storageclose(sd); + } + } + else + storage_premiumStorage_saved(sd); +} + +/** + * Force save then close the premium storage + * @param sd Player who has the storage + * @author [Cydh] + **/ +void storage_premiumStorage_quit(struct map_session_data *sd) { + nullpo_retv(sd); + + if (!&sd->premiumStorage) + return; + + intif_storage_save(sd, &sd->premiumStorage); +} diff --git a/src/map/storage.h b/src/map/storage.h index 6ef94f5259..165d441184 100644 --- a/src/map/storage.h +++ b/src/map/storage.h @@ -10,12 +10,25 @@ struct item; //#include "map.h" struct map_session_data; -int storage_delitem(struct map_session_data* sd, int n, int amount); +extern struct s_storage_table *storage_db; +extern int storage_count; + +enum e_storage_add { + STORAGE_ADD_OK, + STORAGE_ADD_NOROOM, + STORAGE_ADD_NOACCESS, + STORAGE_ADD_INVALID, +}; + +const char *storage_getName(uint8 id); +bool storage_exists(uint8 id); + +int storage_delitem(struct map_session_data* sd, struct s_storage *stor, int index, int amount); int storage_storageopen(struct map_session_data *sd); -void storage_storageadd(struct map_session_data *sd,int index,int amount); -void storage_storageget(struct map_session_data *sd,int index,int amount); -void storage_storageaddfromcart(struct map_session_data *sd,int index,int amount); -void storage_storagegettocart(struct map_session_data *sd,int index,int amount); +void storage_storageadd(struct map_session_data *sd, struct s_storage *stor, int index, int amount); +void storage_storageget(struct map_session_data *sd, struct s_storage *stor, int index, int amount); +void storage_storageaddfromcart(struct map_session_data *sd, struct s_storage *stor, int index, int amount); +void storage_storagegettocart(struct map_session_data *sd, struct s_storage *stor, int index, int amount); void storage_storageclose(struct map_session_data *sd); void storage_sortitem(struct item* items, unsigned int size); void do_init_storage(void); @@ -39,6 +52,14 @@ void storage_guild_storage_quit(struct map_session_data *sd,int flag); bool storage_guild_storagesave(uint32 account_id, int guild_id, int flag); void storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved. +// Premium Storage [Cydh] +void storage_premiumStorage_open(struct map_session_data *sd); +bool storage_premiumStorage_load(struct map_session_data *sd, uint8 num, uint8 mode); +void storage_premiumStorage_save(struct map_session_data *sd); +void storage_premiumStorage_saved(struct map_session_data *sd); +void storage_premiumStorage_close(struct map_session_data *sd); +void storage_premiumStorage_quit(struct map_session_data *sd); + int compare_item(struct item *a, struct item *b); #endif /* _STORAGE_H_ */ diff --git a/src/map/unit.c b/src/map/unit.c index e73bf89f7a..966d2d64aa 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2912,6 +2912,8 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, storage_storage_quit(sd,0); else if (sd->state.storage_flag == 2) storage_guild_storage_quit(sd, 0); + else if (sd->state.storage_flag == 3) + storage_premiumStorage_quit(sd); sd->state.storage_flag = 0; //Force close it when being warped. } diff --git a/vcproj-10/map-server.vcxproj b/vcproj-10/map-server.vcxproj index 641c198152..5006fcc76d 100644 --- a/vcproj-10/map-server.vcxproj +++ b/vcproj-10/map-server.vcxproj @@ -282,6 +282,7 @@ + diff --git a/vcproj-12/map-server.vcxproj b/vcproj-12/map-server.vcxproj index 9860aeb46a..a57fb8c1c6 100644 --- a/vcproj-12/map-server.vcxproj +++ b/vcproj-12/map-server.vcxproj @@ -286,6 +286,7 @@ + diff --git a/vcproj-13/map-server.vcxproj b/vcproj-13/map-server.vcxproj index 55b9440719..eaf55d1d87 100644 --- a/vcproj-13/map-server.vcxproj +++ b/vcproj-13/map-server.vcxproj @@ -286,6 +286,7 @@ + diff --git a/vcproj-14/map-server.vcxproj b/vcproj-14/map-server.vcxproj index 9fd854c7e0..b220a090fb 100644 --- a/vcproj-14/map-server.vcxproj +++ b/vcproj-14/map-server.vcxproj @@ -284,6 +284,7 @@ +