Ver Fonte

Merge pull request #197 from rathena/hotfix/bug9338
* Changed Guild/Party Bound Item Retrieval Methods:
- Fixes bugreport:9338
- Now resend the list to map-server from char/inter-server to avoid "weird" item allocation on guild storage. Example, previously you will meet stacked item like GUID does, but it shouldn't!
- Also will check the maximum amount that can be store in guild store. If guild storage is full (the slots or item stack limit is reach) the rest value/item will be dropped.
- Account of kicked guild member won't be kicked from server any more if the player idle on character select window (just prevents player to login when retrieving items are in progress)
- Delete guild bound items from guild master when guild is disbanded! Previously, the bound type just changed to NONE. Also for party bound item.
- If kicked guild/party member is trading bound item when be kicked, the trade will be cancelled!
- If the guild member is being kicked when open guild storage, storage will be closed first waiting for next item retrieval.
- Now when guild storage is opened, storage_status (changed the var to 'opened') will be used to hold char_id who opens it.
* Misc:
- Cleanup guild storage prefix functions, changed them to "gstorage_*"
- Added picklog type 'B' for logging deleted bound item because of kicked from guild/party.
- Updated documentation for new used packet 0x3857

aleos89 há 10 anos atrás
pai
commit
a683e005d7

+ 14 - 0
doc/packet_interserv.txt

@@ -2021,6 +2021,20 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
 	desc:
 		- Acknowledge the good deletion of the bound item
 
+0x3857
+	Type: IZ
+	Structure: <cmd>.W <size>.W <count>.W <guild_id>.W { <items>.?B }*MAX_INVENTORY
+	index: 0,2,4,6,8
+	len: variable: 8+items
+	parameter:
+		- cmd : packet identification (0x3856)
+		- size
+		- count : number of item retrieved
+		- guild_id
+		- items: retreived guild bound items
+	desc:
+		- Ask map-server to process the retreived guild bound items from expelled member
+
 0x3860
 	Type: IZ
 	Structure: <cmd>.W <size>.W <char_id>.L <quest>.?B

+ 1 - 0
sql-files/upgrades/upgrade_20150103_log.sql

@@ -0,0 +1 @@
+ALTER TABLE `picklog` MODIFY `type` ENUM('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$') NOT NULL DEFAULT 'S';

+ 17 - 0
src/char/char.c

@@ -1809,7 +1809,24 @@ void char_disconnect_player(uint32 account_id)
 		set_eof(i);
 }
 
+/**
+* Set 'flag' value of char_session_data
+* @param account_id
+* @param value
+* @param set True: set the value by using '|= val', False: unset the value by using '&= ~val'
+**/
+void char_set_session_flag_(int account_id, int val, bool set) {
+	int i;
+	struct char_session_data* sd;
 
+	ARR_FIND(0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == account_id);
+	if (i < fd_max) {
+		if (set)
+			sd->flag |= val;
+		else
+			sd->flag &= ~val;
+	}
+}
 
 void char_auth_ok(int fd, struct char_session_data *sd) {
 	struct online_char_data* character;

+ 5 - 0
src/char/char.h

@@ -199,6 +199,7 @@ struct char_session_data {
 	uint8 isvip;
 	time_t unban_time[MAX_CHARS];
 	int charblock_timer;
+	uint8 flag; // &1 - Retrieving guild bound items
 };
 
 
@@ -263,6 +264,10 @@ int char_make_new_char_sql(struct char_session_data* sd, char* name_, int slot,
 int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style);
 #endif
 
+void char_set_session_flag_(int account_id, int val, bool set);
+#define char_set_session_flag(account_id, val)   ( char_set_session_flag_((account_id), (val), true)  )
+#define char_unset_session_flag(account_id, val) ( char_set_session_flag_((account_id), (val), false) )
+
 //For use in packets that depend on an sd being present [Skotlex]
 #define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } }
 

+ 9 - 0
src/char/char_clif.c

@@ -704,6 +704,15 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
 		char_id = atoi(data);
 		Sql_FreeResult(sql_handle);
 
+		// Prevent select a char while retrieving guild bound items
+		if (sd->flag&1) {
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x6c;
+			WFIFOB(fd,2) = 0; // rejected from server
+			WFIFOSET(fd,3);
+			return 1;
+		}
+
 		/* client doesn't let it get to this point if you're banned, so its a forged packet */
 		if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(NULL) ) {
 			WFIFOHEAD(fd,3);

+ 103 - 135
src/char/int_storage.c

@@ -229,47 +229,80 @@ int mapif_parse_SaveGuildStorage(int fd)
 	mapif_save_guild_storage_ack(fd, RFIFOL(fd,4), guild_id, 1);
 	return 0;
 }
+
 #ifdef BOUND_ITEMS
-int mapif_itembound_ack(int fd, int aid, int guild_id)
+/**
+* IZ 0x3856 <account_id>.L <guild_id>.W
+* Tells map-server if the process if complete, unlock the guild storage
+*/
+static void mapif_itembound_ack(int fd, int account_id, int guild_id)
 {
 	WFIFOHEAD(fd,8);
 	WFIFOW(fd,0) = 0x3856;
-	WFIFOL(fd,2) = aid;
+	WFIFOL(fd,2) = account_id;
 	WFIFOW(fd,6) = guild_id;
 	WFIFOSET(fd,8);
-	return 0;
+	char_unset_session_flag(account_id, 1);
+}
+
+/**
+* IZ 0x3857 <size>.W <count>.W <guild_id>.W { <item>.?B }.*MAX_INVENTORY
+* Send the retrieved guild bound items to map-server, store them to guild storage.
+* By using this method, stackable items will looks how it should be, and overflowed
+* item's stack won't disturbs the guild storage table and the leftover items (when
+* storage is full) will be discarded.
+* @param fd
+* @param guild_id
+* @param items[]
+* @param count
+* @author [Cydh]
+*/
+static void mapif_itembound_store2gstorage(int fd, int guild_id, struct item items[], unsigned short count) {
+	int size = 8 + sizeof(struct item) * MAX_INVENTORY, i;
+
+	WFIFOHEAD(fd, size);
+	WFIFOW(fd, 0) = 0x3857;
+	WFIFOW(fd, 2) = size;
+	WFIFOW(fd, 6) = guild_id;
+	for (i = 0; i < count && i < MAX_INVENTORY; i++) {
+		if (!&items[i])
+			continue;
+		memcpy(WFIFOP(fd, 8 + (i * sizeof(struct item))), &items[i], sizeof(struct item));
+	}
+	WFIFOW(fd, 4) = i;
+	WFIFOSET(fd, size);
 }
 
-//------------------------------------------------
-//Guild bound items pull for offline characters [Akinari]
-//------------------------------------------------
+/**
+* ZI 0x3056 <char_id>.L <account_id>.L <guild_id>.W
+* Pulls guild bound items for offline characters
+* @author [Akinari]
+*/
 int mapif_parse_itembound_retrieve(int fd)
 {
 	StringBuf buf;
 	SqlStmt* stmt;
-	struct item item;
-	int j, i = 0, s = 0, bound_qt = 0;
-	bool found = false;
-	struct item items[MAX_INVENTORY];
-	unsigned int bound_item[MAX_INVENTORY] = { 0 };
-	uint32 char_id = RFIFOL(fd,2);
-	int aid = RFIFOL(fd,6);
-	int guild_id = RFIFOW(fd,10);
+	unsigned short i = 0, count = 0;
+	struct item item, items[MAX_INVENTORY];
+	int j, guild_id = RFIFOW(fd,10);
+	uint32 char_id = RFIFOL(fd,2), account_id = RFIFOL(fd,6);
 
 	StringBuf_Init(&buf);
+
+	// Get bound items from player's inventory
 	StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
 	for( j = 0; j < MAX_SLOTS; ++j )
 		StringBuf_Printf(&buf, ", `card%d`", j);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'",schema_config.inventory_db,char_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d' AND `bound` = %d", schema_config.inventory_db,char_id, BOUND_GUILD);
 
 	stmt = SqlStmt_Malloc(sql_handle);
-	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
-	||  SQL_ERROR == SqlStmt_Execute(stmt) )
+	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) ||
+		SQL_ERROR == SqlStmt_Execute(stmt) )
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_Free(stmt);
 		StringBuf_Destroy(&buf);
-		mapif_itembound_ack(fd,aid,guild_id);
+		mapif_itembound_ack(fd,account_id,guild_id);
 		return 1;
 	}
 
@@ -285,83 +318,68 @@ int mapif_parse_itembound_retrieve(int fd)
 	for( j = 0; j < MAX_SLOTS; ++j )
 		SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
 
-	while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) {
-		if(item.bound == BOUND_GUILD) {
-			memcpy(&items[i],&item,sizeof(struct item));
-			i++;
-		}
-	}
+	memset(&items, 0, sizeof(items));
+	while( SQL_SUCCESS == SqlStmt_NextRow(stmt) )
+		memcpy(&items[count++], &item, sizeof(struct item));
 	Sql_FreeResult(sql_handle);
 
-	if(!i) { //No items found - No need to continue
+	ShowInfo("Found '"CL_WHITE"%d"CL_RESET"' guild bound item(s) from CID = "CL_WHITE"%d"CL_RESET", AID = %d, Guild ID = "CL_WHITE"%d"CL_RESET".\n", count, char_id, account_id, guild_id);
+	if (!count) { //No items found - No need to continue
 		StringBuf_Destroy(&buf);
 		SqlStmt_Free(stmt);
-		mapif_itembound_ack(fd,aid,guild_id);
-		return 0;
+		mapif_itembound_ack(fd,account_id,guild_id);
+		return 1;
 	}
 
-	//First we delete the character's items
-	StringBuf_Clear(&buf);
-	StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE",schema_config.inventory_db);
-	for(j=0; j<i; j++) {
-		if( found )
-			StringBuf_AppendStr(&buf, " OR");
-		else
-			found = true;
-		StringBuf_Printf(&buf, " `id`=%d",items[j].id);
-
-		if( items[j].bound && items[j].equip ) {
-			//Only the items that are also stored in `char` `equip`
-			if( (items[j].equip&EQP_HAND_R) ||
-				(items[j].equip&EQP_HAND_L) ||
-				(items[j].equip&EQP_HEAD_TOP) ||
-				(items[j].equip&EQP_HEAD_MID) ||
-				(items[j].equip&EQP_HEAD_LOW) ||
-				(items[j].equip&EQP_GARMENT) ||
-				(items[j].equip&EQP_COSTUME_HEAD_TOP) ||
-				(items[j].equip&EQP_COSTUME_HEAD_MID) ||
-				(items[j].equip&EQP_COSTUME_HEAD_LOW) ||
-				(items[j].equip&EQP_COSTUME_GARMENT) )
-			{
-				bound_item[bound_qt] = items[j].equip;
-				bound_qt++;
-			}
-		}
-	}
+	char_set_session_flag(account_id, 1);
 
-	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
-	||  SQL_ERROR == SqlStmt_Execute(stmt) )
+	// Delete bound items from player's inventory
+	StringBuf_Clear(&buf);
+	StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE `bound` = %d",schema_config.inventory_db, BOUND_GUILD);
+	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) ||
+		SQL_ERROR == SqlStmt_Execute(stmt) )
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_Free(stmt);
 		StringBuf_Destroy(&buf);
-		mapif_itembound_ack(fd,aid,guild_id);
+		mapif_itembound_ack(fd,account_id,guild_id);
 		return 1;
 	}
 
-	if( bound_qt ) { //Removes any view id that was set by an item that was removed
-/* Verifies equip bitmasks (see item.equip) and handles the sql statement */
-#define CHECK_REMOVE(var,mask,token) do { \
-	if( (var)&(mask) ) { \
-		if( (var) != (mask) && s ) StringBuf_AppendStr(&buf, ","); \
-		StringBuf_AppendStr(&buf, "`"#token"`='0'"); \
-		(var) &= ~(mask); \
-		s++; \
-	} \
-} while( 0 )
-
-		StringBuf_Clear(&buf);
-		StringBuf_Printf(&buf, "UPDATE `%s` SET ", schema_config.char_db);
-		for( j = 0; j < bound_qt; j++ ) {
-			//Equips can be at more than one slot at the same time
-			CHECK_REMOVE(bound_item[j],EQP_HAND_R,weapon);
-			CHECK_REMOVE(bound_item[j],EQP_HAND_L,shield);
-			CHECK_REMOVE(bound_item[j],EQP_HEAD_TOP|EQP_COSTUME_HEAD_TOP,head_top);
-			CHECK_REMOVE(bound_item[j],EQP_HEAD_MID|EQP_COSTUME_HEAD_MID,head_mid);
-			CHECK_REMOVE(bound_item[j],EQP_HEAD_LOW|EQP_COSTUME_HEAD_LOW,head_bottom);
-			CHECK_REMOVE(bound_item[j],EQP_GARMENT|EQP_COSTUME_GARMENT,robe);
-		}
-		StringBuf_Printf(&buf, " WHERE `char_id`='%d'", char_id);
+	// Send the deleted items to map-server to store them in guild storage [Cydh]
+	mapif_itembound_store2gstorage(fd, guild_id, items, count);
+
+	// Verifies equip bitmasks (see item.equip) and handles the sql statement
+#define CHECK_REMOVE(var,mask,token,num) {\
+	if ((var)&(mask) && !(j&(num))) {\
+		if (j)\
+			StringBuf_AppendStr(&buf, ",");\
+		StringBuf_AppendStr(&buf, "`"#token"`='0'");\
+		j |= (1<<num);\
+	}\
+}
+
+	StringBuf_Clear(&buf);
+	j = 0;
+	for (i = 0; i < count && i < MAX_INVENTORY; i++) {
+		if (!&items[i] || !items[i].equip)
+			continue;
+		// Equips can be at more than one slot at the same time
+		CHECK_REMOVE(items[i].equip, EQP_HAND_R, weapon, 0);
+		CHECK_REMOVE(items[i].equip, EQP_HAND_L, shield, 1);
+		CHECK_REMOVE(items[i].equip, EQP_HEAD_TOP|EQP_COSTUME_HEAD_TOP, head_top, 2);
+		CHECK_REMOVE(items[i].equip, EQP_HEAD_MID|EQP_COSTUME_HEAD_MID, head_mid, 3);
+		CHECK_REMOVE(items[i].equip, EQP_HEAD_LOW|EQP_COSTUME_HEAD_LOW, head_bottom, 4);
+		CHECK_REMOVE(items[i].equip, EQP_GARMENT|EQP_COSTUME_GARMENT, robe, 5);
+	}
+
+#undef CHECK_REMOVE
+
+	// Update player's view
+	if (j) {
+		StringBuf buf2;
+		StringBuf_Init(&buf2);
+		StringBuf_Printf(&buf2, "UPDATE `%s` SET %s WHERE `char_id`='%d", schema_config.char_db, StringBuf_Value(&buf), char_id);
 
 		if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) ||
 			SQL_ERROR == SqlStmt_Execute(stmt) )
@@ -369,67 +387,17 @@ int mapif_parse_itembound_retrieve(int fd)
 			SqlStmt_ShowDebug(stmt);
 			SqlStmt_Free(stmt);
 			StringBuf_Destroy(&buf);
-			mapif_itembound_ack(fd,aid,guild_id);
+			StringBuf_Destroy(&buf2);
+			mapif_itembound_ack(fd,account_id,guild_id);
 			return 1;
 		}
-#undef CHECK_REMOVE
-	}
-
-	//Now let's update the guild storage with those deleted items
-	//@TODO/FIXME:
-	//This approach is basically the same as the one from memitemdata_to_sql, but
-	//the latter compares current database values and this is not needed in this case
-	//maybe sometime separate memitemdata_to_sql into different methods in order to use
-	//call that function here as well [Panikon]
-	found = false;
-	StringBuf_Clear(&buf);
-	StringBuf_Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `equip`, `identify`, `refine`,"
-		"`attribute`, `expire_time`, `bound`", schema_config.guild_storage_db);
-	for( s = 0; s < MAX_SLOTS; ++s )
-		StringBuf_Printf(&buf, ", `card%d`", s);
-	StringBuf_AppendStr(&buf, ") VALUES ");
-
-	for( j = 0; j < i; ++j ) {
-		if( found )
-			StringBuf_AppendStr(&buf, ",");
-		else
-			found = true;
-
-		StringBuf_Printf(&buf, "('%d', '%hu', '%d', '%d', '%d', '%d', '%d', '%d', '%d'",
-			guild_id, items[j].nameid, items[j].amount, items[j].equip, items[j].identify, items[j].refine,
-			items[j].attribute, items[j].expire_time, items[j].bound);
-		for( s = 0; s < MAX_SLOTS; ++s )
-			StringBuf_Printf(&buf, ", '%hu'", items[j].card[s]);
-		StringBuf_AppendStr(&buf, ")");
-	}
-
-	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
-	||  SQL_ERROR == SqlStmt_Execute(stmt) )
-	{
-		SqlStmt_ShowDebug(stmt);
-		SqlStmt_Free(stmt);
-		StringBuf_Destroy(&buf);
-		mapif_itembound_ack(fd,aid,guild_id);
-		return 1;
+		StringBuf_Destroy(&buf2);
 	}
 
 	StringBuf_Destroy(&buf);
 	SqlStmt_Free(stmt);
 
-	//Finally reload storage and tell map we're done
-	mapif_load_guild_storage(fd,aid,guild_id,0);
-
-	//If character is logged in char, disconnect, 
-	/* @CHECKME [lighta]
-	 * I suppose this was an attempt to avoid item duplication if the expelled user reconnect during the operation.
-	 * well it's kinda ugly to expel someone like this, so I consider this as a hack.
-	 * we better flag them so that they not allowed to reconnect during operation or flag it so we will flush those item on ram with the map ack.
-	 * both way seem nicer for player.
-	 */
-	char_disconnect_player(aid);
-
-	//Tell map-server the operation is over and it can unlock the storage
-	mapif_itembound_ack(fd,aid,guild_id);
+	char_unset_session_flag(account_id, 1);
 	return 0;
 }
 #endif

+ 3 - 3
src/common/mmo.h

@@ -278,12 +278,12 @@ struct storage_data {
 };
 
 struct guild_storage {
-	int dirty;
+	bool dirty;
 	int guild_id;
-	short storage_status;
 	short storage_amount;
 	struct item items[MAX_GUILD_STORAGE];
-	unsigned short lock;
+	bool locked;
+	int opened; /// Holds the char_id that open the storage
 };
 
 struct s_pet {

+ 12 - 6
src/map/atcommand.c

@@ -920,7 +920,7 @@ ACMD_FUNC(guildstorage)
 		return -1;
 	}
 
-	storage_guild_storageopen(sd);
+	gstorage_storageopen(sd);
 	clif_displaymessage(fd, msg_txt(sd,920)); // Guild storage opened.
 	return 0;
 }
@@ -5433,18 +5433,24 @@ ACMD_FUNC(cleargstorage)
 		return -1;
 	}
 
-	gstorage = guild2storage2(sd->status.guild_id);
+	gstorage = gstorage_get_storage(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;
 	}
 
+	if (gstorage->opened) {
+		struct map_session_data *tsd = map_charid2sd(gstorage->opened);
+		if (tsd)
+			gstorage_storageclose(tsd);
+	}
+
 	j = gstorage->storage_amount;
-	gstorage->lock = 1; // Lock @gstorage: do not allow any item to be retrieved or stored from any guild member
+	gstorage->locked = true; // Lock @gstorage: do not allow any item to be retrieved or stored from any guild member
 	for (i = 0; i < j; ++i) {
-		guild_storage_delitem(sd, gstorage, i, gstorage->items[i].amount);
+		gstorage_delitem(sd, gstorage, i, gstorage->items[i].amount);
 	}
-	storage_guild_storageclose(sd);
-	gstorage->lock = 0; // Cleaning done, release lock
+	gstorage_storageclose(sd);
+	gstorage->locked = false; // Cleaning done, release lock
 
 	clif_displaymessage(fd, msg_txt(sd,1395)); // Your guild storage was cleaned.
 	return 0;

+ 1 - 1
src/map/chrif.c

@@ -293,7 +293,7 @@ int chrif_save(struct map_session_data *sd, int flag) {
 
 	//For data sync
 	if (sd->state.storage_flag == 2)
-		storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
+		gstorage_storagesave(sd->status.account_id, sd->status.guild_id, flag);
 
 	if (flag)
 		sd->state.storage_flag = 0; //Force close it.

+ 37 - 37
src/map/clif.c

@@ -2198,30 +2198,30 @@ void clif_additem(struct map_session_data *sd, int n, int amount, unsigned char
 	WFIFOHEAD(fd,packet_len(header));
 	if( fail )
 	{
-		WFIFOW(fd,offs+0)=header;
-		WFIFOW(fd,offs+2)=n+2;
-		WFIFOW(fd,offs+4)=amount;
-		WFIFOW(fd,offs+6)=0;
-		WFIFOB(fd,offs+8)=0;
-		WFIFOB(fd,offs+9)=0;
-		WFIFOB(fd,offs+10)=0;
-		WFIFOW(fd,offs+11)=0;
-		WFIFOW(fd,offs+13)=0;
-		WFIFOW(fd,offs+15)=0;
-		WFIFOW(fd,offs+17)=0;
+		WFIFOW(fd,offs+0) = header;
+		WFIFOW(fd,offs+2) = n+2;
+		WFIFOW(fd,offs+4) = amount;
+		WFIFOW(fd,offs+6) = 0;
+		WFIFOB(fd,offs+8) = 0;
+		WFIFOB(fd,offs+9) = 0;
+		WFIFOB(fd,offs+10) = 0;
+		WFIFOW(fd,offs+11) = 0;
+		WFIFOW(fd,offs+13) = 0;
+		WFIFOW(fd,offs+15) = 0;
+		WFIFOW(fd,offs+17) = 0;
 #if PACKETVER < 20120925
-		WFIFOW(fd,offs+19)=0;
+		WFIFOW(fd,offs+19) = 0;
 #else
-		WFIFOL(fd,offs+19)=0;
+		WFIFOL(fd,offs+19) = 0;
 		offs += 2;
 #endif
-		WFIFOB(fd,offs+21)=0;
-		WFIFOB(fd,offs+22)=fail;
+		WFIFOB(fd,offs+21) = 0;
+		WFIFOB(fd,offs+22) = fail;
 #if PACKETVER >= 20061218
-		WFIFOL(fd,offs+23)=0;
+		WFIFOL(fd,offs+23) = 0;
 #endif
 #if PACKETVER >= 20071002
-		WFIFOW(fd,offs+27)=0;  //  HireExpireDate
+		WFIFOW(fd,offs+27) = 0;  //  HireExpireDate
 #endif
 	}
 	else
@@ -2229,31 +2229,31 @@ void clif_additem(struct map_session_data *sd, int n, int amount, unsigned char
 		if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL )
 			return;
 
-		WFIFOW(fd,offs+0)=header;
-		WFIFOW(fd,offs+2)=n+2;
-		WFIFOW(fd,offs+4)=amount;
+		WFIFOW(fd,offs+0) = header;
+		WFIFOW(fd,offs+2) = n+2;
+		WFIFOW(fd,offs+4) = amount;
 		if (sd->inventory_data[n]->view_id > 0)
-			WFIFOW(fd,offs+6)=sd->inventory_data[n]->view_id;
+			WFIFOW(fd,offs+6) = sd->inventory_data[n]->view_id;
 		else
-			WFIFOW(fd,offs+6)=sd->status.inventory[n].nameid;
-		WFIFOB(fd,offs+8)=sd->status.inventory[n].identify;
-		WFIFOB(fd,offs+9)=sd->status.inventory[n].attribute;
-		WFIFOB(fd,offs+10)=sd->status.inventory[n].refine;
+			WFIFOW(fd,offs+6) = sd->status.inventory[n].nameid;
+		WFIFOB(fd,offs+8) = sd->status.inventory[n].identify;
+		WFIFOB(fd,offs+9) = sd->status.inventory[n].attribute;
+		WFIFOB(fd,offs+10) = sd->status.inventory[n].refine;
 		clif_addcards(WFIFOP(fd,offs+11), &sd->status.inventory[n]);
 #if PACKETVER < 20120925
-		WFIFOW(fd,offs+19)=pc_equippoint(sd,n);
+		WFIFOW(fd,offs+19) = pc_equippoint(sd,n);
 #else
-		WFIFOL(fd,offs+19)=pc_equippoint(sd,n);
+		WFIFOL(fd,offs+19) = pc_equippoint(sd,n);
 		offs += 2;
 #endif
-		WFIFOB(fd,offs+21)=itemtype(sd->inventory_data[n]->nameid);
-		WFIFOB(fd,offs+22)=fail;
+		WFIFOB(fd,offs+21) = itemtype(sd->inventory_data[n]->nameid);
+		WFIFOB(fd,offs+22) = fail;
 #if PACKETVER >= 20061218
-		WFIFOL(fd,offs+23)=sd->status.inventory[n].expire_time;
+		WFIFOL(fd,offs+23) = sd->status.inventory[n].expire_time;
 #endif
 #if PACKETVER >= 20071002
 		/* Yellow color only for non-stackable item */
-		WFIFOW(fd,offs+27)=(sd->status.inventory[n].bound && !itemdb_isstackable(sd->status.inventory[n].nameid)) ? BOUND_DISPYELLOW : sd->inventory_data[n]->flag.bindOnEquip ? BOUND_ONEQUIP : 0;
+		WFIFOW(fd,offs+27) = (sd->status.inventory[n].bound && !itemdb_isstackable(sd->status.inventory[n].nameid)) ? BOUND_DISPYELLOW : sd->inventory_data[n]->flag.bindOnEquip ? BOUND_ONEQUIP : 0;
 #endif
 	}
 
@@ -8676,7 +8676,7 @@ void clif_refresh_storagewindow(struct map_session_data *sd) {
 	// Notify the client that the gstorage is open otherwise it will
 	// remain locked forever and nobody will be able to access it
 	if( sd->state.storage_flag == 2 ) {
-		struct guild_storage *gstor = guild2storage2(sd->status.guild_id);
+		struct guild_storage *gstor = gstorage_get_storage(sd->status.guild_id);
 
 		if( !gstor ) // Shouldn't happen. The information should already be at the map-server
 			intif_request_guild_storage(sd->status.account_id, sd->status.guild_id);
@@ -11915,7 +11915,7 @@ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd)
 		storage_storageadd(sd, item_index, item_amount);
 	else
 	if (sd->state.storage_flag == 2)
-		storage_guild_storageadd(sd, item_index, item_amount);
+		gstorage_storageadd(sd, item_index, item_amount);
 }
 
 
@@ -11934,7 +11934,7 @@ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd)
 	if (sd->state.storage_flag == 1)
 		storage_storageget(sd, item_index, item_amount);
 	else if(sd->state.storage_flag == 2)
-		storage_guild_storageget(sd, item_index, item_amount);
+		gstorage_storageget(sd, item_index, item_amount);
 }
 
 
@@ -11954,7 +11954,7 @@ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd){
 	if (sd->state.storage_flag == 1)
 		storage_storageaddfromcart(sd, idx, amount);
 	else if (sd->state.storage_flag == 2)
-		storage_guild_storageaddfromcart(sd, idx, amount);
+		gstorage_storageaddfromcart(sd, idx, amount);
 }
 
 
@@ -11974,7 +11974,7 @@ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd){
 		storage_storagegettocart(sd, idx, amount);
 	else
 	if (sd->state.storage_flag == 2)
-		storage_guild_storagegettocart(sd, idx, amount);
+		gstorage_storagegettocart(sd, idx, amount);
 }
 
 
@@ -11986,7 +11986,7 @@ void clif_parse_CloseKafra(int fd, struct map_session_data *sd)
 		storage_storageclose(sd);
 	else
 	if( sd->state.storage_flag == 2 )
-		storage_guild_storageclose(sd);
+		gstorage_storageclose(sd);
 }
 
 

+ 92 - 41
src/map/guild.c

@@ -19,6 +19,8 @@
 #include "pc.h"
 #include "intif.h"
 #include "channel.h"
+#include "log.h"
+#include "trade.h"
 
 #include <stdlib.h>
 
@@ -39,7 +41,6 @@ struct eventlist {
 #define GUILD_PAYEXP_LIST 8192 //The maximum number of cache
 
 //Guild EXP cache
-
 struct guild_expcache {
 	int guild_id, account_id, char_id;
 	uint64 exp;
@@ -408,6 +409,20 @@ int guild_npc_request_info(int guild_id,const char *event) {
 	return guild_request_info(guild_id);
 }
 
+/**
+ * Close trade window if party member is kicked when trade a party bound item
+ * @param sd
+ **/
+static void guild_trade_bound_cancel(struct map_session_data *sd) {
+#ifdef BOUND_ITEMS
+	nullpo_retv(sd);
+	if (sd->state.isBoundTrading&(1<<BOUND_GUILD))
+		trade_tradecancel(sd);
+#else
+	;
+#endif
+}
+
 //Confirmation of the character belongs to guild
 int guild_check_member(struct guild *g) {
 	int i;
@@ -740,6 +755,7 @@ int guild_leave(struct map_session_data* sd, int guild_id, uint32 account_id, ui
 		((agit_flag || agit2_flag) && map[sd->bl.m].flag.gvg_castle))
 		return 0;
 
+	guild_trade_bound_cancel(sd);
 	intif_guild_leave(sd->status.guild_id, sd->status.account_id, sd->status.char_id,0,mes);
 	return 0;
 }
@@ -773,12 +789,24 @@ int guild_expulsion(struct map_session_data* sd, int guild_id, uint32 account_id
 
 	// find the member and perform expulsion
 	i = guild_getindex(g, account_id, char_id);
-	if( i != -1 && strcmp(g->member[i].name,g->master) != 0 ) //Can't expel the GL!
+	if( i != -1 && strcmp(g->member[i].name,g->master) != 0 ) { //Can't expel the GL!
+		if (tsd)
+			guild_trade_bound_cancel(tsd);
 		intif_guild_leave(g->guild_id,account_id,char_id,1,mes);
+	}
 
 	return 0;
 }
 
+/**
+* A confirmation from inter-serv that player is kicked successfully
+* @param guild_Id
+* @param account_id
+* @param char_id
+* @param flag
+* @param name
+* @param mes
+*/
 int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int flag, const char* name, const char* mes) {
 	int i;
 	struct guild* g = guild_search(guild_id);
@@ -815,7 +843,7 @@ int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int f
 	if(sd != NULL && sd->status.guild_id == guild_id) {
 		// do stuff that needs the guild_id first, BEFORE we wipe it
 		if (sd->state.storage_flag == 2) //Close the guild storage.
-			storage_guild_storageclose(sd);
+			gstorage_storageclose(sd);
 		guild_send_dot_remove(sd);
 		channel_pcquit(sd,3); //leave guild and ally chan
 		sd->status.guild_id = 0;
@@ -833,35 +861,44 @@ int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int f
 }
 
 #ifdef BOUND_ITEMS
-void guild_retrieveitembound(uint32 char_id,int aid,int guild_id) {
-	TBL_PC *sd = map_id2sd(aid);
-	if(sd){ //Character is online
+/**
+* Retrieve guild bound items from kicked member
+* @param char_id
+* @param account_id
+* @param guild_id
+*/
+void guild_retrieveitembound(uint32 char_id, uint32 account_id, int guild_id) {
+	TBL_PC *sd = map_charid2sd(char_id);
+	if (sd) { //Character is online
 		int idxlist[MAX_INVENTORY];
 		int j;
 		j = pc_bound_chk(sd,BOUND_GUILD,idxlist);
-		if(j) {
-			struct guild_storage* stor = guild2storage(sd->status.guild_id);
+		if (j) {
+			struct guild_storage* stor = gstorage_guild2storage(sd->status.guild_id);
 			int i;
-			for(i=0;i<j;i++) { //Loop the matching items, guild_storage_additem takes care of opening storage
-				if(stor)
-					guild_storage_additem(sd,stor,&sd->status.inventory[idxlist[i]],sd->status.inventory[idxlist[i]].amount);
+			// Close the storage first if someone open it
+			if (stor && stor->opened) {
+				struct map_session_data *tsd = map_charid2sd(stor->opened);
+				if (tsd)
+					gstorage_storageclose(tsd);
+			}
+			for (i = 0; i < j; i++) { //Loop the matching items, gstorage_additem takes care of opening storage
+				if (stor)
+					gstorage_additem(sd,stor,&sd->status.inventory[idxlist[i]],sd->status.inventory[idxlist[i]].amount);
 				pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,4,LOG_TYPE_GSTORAGE);
 			}
-			storage_guild_storageclose(sd); //Close and save the storage
+			gstorage_storageclose(sd); //Close and save the storage
 		}
 	} else { //Character is offline, ask char server to do the job
-		struct guild_storage* stor = guild2storage2(guild_id);
+		struct guild_storage* stor = gstorage_get_storage(guild_id);
 		struct guild *g = guild_search(guild_id);
 		nullpo_retv(g);
-		if(stor && stor->storage_status == 1) { //Someone is in guild storage, close them
-			int i;
-			for(i=0; i<g->max_member; i++){
-				TBL_PC *pl_sd = g->member[i].sd;
-				if(pl_sd && pl_sd->state.storage_flag == 2)
-					storage_guild_storageclose(pl_sd);
-			}
+		if(stor && stor->opened) { //Someone is in guild storage, close them
+			struct map_session_data *tsd = map_charid2sd(stor->opened);
+			if (tsd)
+				gstorage_storageclose(tsd);
 		}
-		intif_itembound_req(char_id,aid,guild_id);
+		intif_itembound_guild_retrieve(char_id,account_id,guild_id);
 	}
 }
 #endif
@@ -1643,14 +1680,14 @@ int guild_broken(int guild_id,int flag) {
 	struct guild *g = guild_search(guild_id);
 	int i;
 
-	if(flag!=0 || g==NULL)
+	if (flag != 0 || g == NULL)
 		return 0;
 
-	for(i=0;i<g->max_member;i++){	// Destroy all relationships
+	for (i = 0; i < g->max_member; i++){	// Destroy all relationships
 		struct map_session_data *sd = g->member[i].sd;
 		if(sd != NULL){
 			if(sd->state.storage_flag == 2)
-				storage_guild_storage_quit(sd,1);
+				gstorage_storage_quit(sd,1);
 			sd->status.guild_id=0;
 			sd->guild = NULL;
 			sd->state.gmaster_flag = 0;
@@ -1665,7 +1702,7 @@ int guild_broken(int guild_id,int flag) {
 
 	guild_db->foreach(guild_db,guild_broken_sub,guild_id);
 	castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
-	guild_storage_delete(guild_id);
+	gstorage_delete(guild_id);
 	if( channel_config.ally_enable ) {
 		channel_delete(g->channel);
 	}
@@ -1673,7 +1710,10 @@ int guild_broken(int guild_id,int flag) {
 	return 0;
 }
 
-//Changes the Guild Master to the specified player. [Skotlex]
+/** Changes the Guild Master to the specified player. [Skotlex]
+* @param guild_id
+* @param sd New guild master
+*/
 int guild_gm_change(int guild_id, struct map_session_data *sd) {
 	struct guild *g;
 	nullpo_ret(sd);
@@ -1681,7 +1721,7 @@ int guild_gm_change(int guild_id, struct map_session_data *sd) {
 	if (sd->status.guild_id != guild_id)
 		return 0;
 
-	g=guild_search(guild_id);
+	g = guild_search(guild_id);
 
 	nullpo_ret(g);
 
@@ -1693,7 +1733,11 @@ int guild_gm_change(int guild_id, struct map_session_data *sd) {
 	return 1;
 }
 
-//Notification from Char server that a guild's master has changed. [Skotlex]
+/** Notification from Char server that a guild's master has changed. [Skotlex]
+* @param guild_id
+* @param account_id
+* @param char_id
+*/
 int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id) {
 	struct guild *g;
 	struct guild_member gm;
@@ -1743,9 +1787,10 @@ int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id) {
 	return 1;
 }
 
-/*====================================================
- * Guild disbanded
- *---------------------------------------------------*/
+/** Disband a guild
+* @param sd Player who breaks the guild
+* @param name Guild name
+*/
 int guild_break(struct map_session_data *sd,char *name) {
 	struct guild *g;
 	struct unit_data *ud;
@@ -1757,25 +1802,25 @@ int guild_break(struct map_session_data *sd,char *name) {
 
 	nullpo_ret(sd);
 
-	if( (g=sd->guild)==NULL )
+	if ((g=sd->guild)==NULL)
 		return 0;
-	if(strcmp(g->name,name)!=0)
+	if (strcmp(g->name,name) != 0)
 		return 0;
-	if(!sd->state.gmaster_flag)
+	if (!sd->state.gmaster_flag)
 		return 0;
-	for(i=0;i<g->max_member;i++){
+	for (i = 0; i < g->max_member; i++) {
 		if(	g->member[i].account_id>0 && (
 			g->member[i].account_id!=sd->status.account_id ||
 			g->member[i].char_id!=sd->status.char_id ))
 			break;
 	}
-	if(i<g->max_member){
+	if (i < g->max_member) {
 		clif_guild_broken(sd,2);
 		return 0;
 	}
 
 	/* Regardless of char server allowing it, we clear the guild master's auras */
-	if((ud = unit_bl2ud(&sd->bl))) {
+	if ((ud = unit_bl2ud(&sd->bl))) {
 		int count = 0;
 		struct skill_unit_group *group[4];
 
@@ -1792,15 +1837,15 @@ int guild_break(struct map_session_data *sd,char *name) {
 					break;
 			}
 		}
-		for(i = 0; i < count; i++)
+		for (i = 0; i < count; i++)
 			skill_delunitgroup(group[i]);
 	}
 
 #ifdef BOUND_ITEMS
 	//Guild bound item check - Removes the bound flag
 	j = pc_bound_chk(sd,BOUND_GUILD,idxlist);
-	for(i=0;i<j;i++)
-		sd->status.inventory[idxlist[i]].bound = BOUND_NONE;
+	for(i = 0; i < j; i++)
+		pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_BOUND_REMOVAL);
 #endif
 
 	intif_guild_break(g->guild_id);
@@ -1911,6 +1956,9 @@ void guild_castle_reconnect_sub(void *key, void *data, va_list ap) {
  * Saves pending guild castle data changes when char-server is
  * disconnected.
  * On reconnect pushes all changes to char-server for saving.
+ * @param castle_id
+ * @param index
+ * @param value
  */
 void guild_castle_reconnect(int castle_id, int index, int value) {
 	static struct linkdb_node *gc_save_pending = NULL;
@@ -1926,7 +1974,10 @@ void guild_castle_reconnect(int castle_id, int index, int value) {
 	}
 }
 
-// Load castle data then invoke OnAgitInit* on last
+/** Load castle data then invoke OnAgitInit* on last
+* @param len
+* @param gc Guild Castle data
+*/
 int guild_castledataloadack(int len, struct guild_castle *gc) {
 	int i;
 	int n = (len-4) / sizeof(struct guild_castle);

+ 1 - 1
src/map/guild.h

@@ -107,7 +107,7 @@ void guild_flags_clear(void);
 
 void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv);
 #ifdef BOUND_ITEMS
-void guild_retrieveitembound(uint32 char_id,int aid,int guild_id);
+void guild_retrieveitembound(uint32 char_id,uint32 account_id,int guild_id);
 #endif
 
 void do_final_guild(void);

+ 53 - 19
src/map/intif.c

@@ -30,7 +30,7 @@ static const int packet_len_table[]={
 	39,-1,15,15, 14,19, 7,-1,  0, 0, 0, 0,  0, 0,  0, 0, //0x3820
 	10,-1,15, 0, 79,19, 7,-1,  0,-1,-1,-1, 14,67,186,-1, //0x3830
 	-1, 0, 0,14,  0, 0, 0, 0, -1,74,-1,11, 11,-1,  0, 0, //0x3840
-	-1,-1, 7, 7,  7,11, 8, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3850  Auctions [Zephyrus] itembound[Akinari]
+	-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, 0, 0,  0, 0,  0, 0, //0x3880
@@ -1333,22 +1333,22 @@ int intif_parse_LoadGuildStorage(int fd)
 
 	guild_id = RFIFOL(fd,8);
 	flag = RFIFOL(fd,12);
-	if(guild_id <= 0)
+	if (guild_id <= 0)
 		return 0;
 
-	sd=map_id2sd( RFIFOL(fd,4) );
-	if( flag ){ //If flag != 0, we attach a player and open the storage
-		if(sd==NULL){
-			ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4));
+	sd = map_id2sd( RFIFOL(fd,4) );
+	if (flag){ //If flag != 0, we attach a player and open the storage
+		if(sd == NULL){
+			ShowError("intif_parse_LoadGuildStorage: user not found (AID: %d)\n",RFIFOL(fd,4));
 			return 0;
 		}
 	}
-	gstor=guild2storage(guild_id);
-	if(!gstor) {
+	gstor = gstorage_guild2storage(guild_id);
+	if (!gstor) {
 		ShowWarning("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id);
 		return 0;
 	}
-	if (gstor->storage_status == 1) { // Already open.. lets ignore this update
+	if (gstor->opened) { // Already open.. lets ignore this update
 		ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1);
 		return 0;
 	}
@@ -1358,13 +1358,13 @@ int intif_parse_LoadGuildStorage(int fd)
 	}
 	if( RFIFOW(fd,2)-13 != sizeof(struct guild_storage) ){
 		ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-13 , sizeof(struct guild_storage));
-		gstor->storage_status = 0;
+		gstor->opened = 0;
 		return 0;
 	}
 
 	memcpy(gstor,RFIFOP(fd,13),sizeof(struct guild_storage));
 	if( flag )
-		storage_guild_storageopen(sd);
+		gstorage_storageopen(sd);
 
 	return 1;
 }
@@ -1376,7 +1376,7 @@ int intif_parse_LoadGuildStorage(int fd)
  */
 int intif_parse_SaveGuildStorage(int fd)
 {
-	storage_guild_storagesaved(/*RFIFOL(fd,2), */RFIFOL(fd,6));
+	gstorage_storagesaved(/*RFIFOL(fd,2), */RFIFOL(fd,6));
 	return 1;
 }
 
@@ -2875,13 +2875,14 @@ void intif_parse_MessageToFD(int fd) {
 #ifdef BOUND_ITEMS
 
 /**
- * Request char-serv to delete some bound item, for non connected cid
+ * ZI 0x3056 <char_id>.L <account_id>.L <guild_id>.W
+ * Request inter-serv to delete some bound item, for non connected cid
  * @param char_id : Char to delete item ID
  * @param aid : Account to delete item ID
  * @param guild_id : Guild of char
  */
-void intif_itembound_req(uint32 char_id, uint32 aid,int guild_id) {
-	struct guild_storage *gstor = guild2storage2(guild_id);
+void intif_itembound_guild_retrieve(uint32 char_id,uint32 account_id,int guild_id) {
+	struct guild_storage *gstor = gstorage_get_storage(guild_id);
 	
 	if( CheckForCharServer() )
 		return;
@@ -2889,10 +2890,12 @@ void intif_itembound_req(uint32 char_id, uint32 aid,int guild_id) {
 	WFIFOHEAD(inter_fd,12);
 	WFIFOW(inter_fd,0) = 0x3056;
 	WFIFOL(inter_fd,2) = char_id;
-	WFIFOL(inter_fd,6) = aid;
+	WFIFOL(inter_fd,6) = account_id;
 	WFIFOW(inter_fd,10) = guild_id;
 	WFIFOSET(inter_fd,12);
-	if(gstor) gstor->lock = 1; //Lock for retrieval process
+	if (gstor)
+		gstor->locked = true; //Lock for retrieval process
+	ShowInfo("Request guild bound item(s) retrieval for CID = "CL_WHITE"%d"CL_RESET", AID = %d, Guild ID = "CL_WHITE"%d"CL_RESET".\n", char_id, account_id, guild_id);
 }
 
 /**
@@ -2903,8 +2906,38 @@ void intif_itembound_req(uint32 char_id, uint32 aid,int guild_id) {
  */
 void intif_parse_itembound_ack(int fd) {
 	int guild_id = RFIFOW(fd,6);
-	struct guild_storage *gstor = guild2storage2(guild_id);
-	if(gstor) gstor->lock = 0; //Unlock now that operation is completed
+	struct guild_storage *gstor = gstorage_get_storage(guild_id);
+	if (gstor)
+		gstor->locked = false; //Unlock now that operation is completed
+}
+
+/**
+* IZ 0x3857 <size>.W <count>.W <guild_id>.W { <item>.?B }.*MAX_INVENTORY
+* Received the retrieved guild bound items from inter-server, store them to guild storage.
+* @param fd
+* @author [Cydh]
+*/
+void intif_parse_itembound_store2gstorage(int fd) {
+	unsigned short i, failed = 0;
+	short count = RFIFOW(fd, 4), guild_id = RFIFOW(fd, 6);
+	struct guild_storage *gstor = gstorage_guild2storage(guild_id);
+
+	if (!gstor) {
+		ShowError("intif_parse_itembound_store2gstorage: Guild '%d' not found.\n", guild_id);
+		return;
+	}
+
+	//@TODO: Gives some actions for item(s) that cannot be stored because storage is full or reach the limit of stack amount
+	for (i = 0; i < count; i++) {
+		struct item *item = (struct item*)RFIFOP(fd, 8 + i*sizeof(struct item));
+		if (!item)
+			continue;
+		if (!gstorage_additem2(gstor, item, item->amount))
+			failed++;
+	}
+	ShowInfo("Retrieved '"CL_WHITE"%d"CL_RESET"' (failed: %d) guild bound item(s) for Guild ID = "CL_WHITE"%d"CL_RESET".\n", count, failed, guild_id);
+	gstor->locked = false;
+	gstor->opened = 0;
 }
 #endif
 
@@ -2998,6 +3031,7 @@ int intif_parse(int fd)
 	//Bound items
 #ifdef BOUND_ITEMS
 	case 0x3856:	intif_parse_itembound_ack(fd); break;
+	case 0x3857:	intif_parse_itembound_store2gstorage(fd); break;
 #endif
 
 	//Quest system

+ 1 - 1
src/map/intif.h

@@ -60,7 +60,7 @@ int intif_guild_emblem(int guild_id, int len, const char *data);
 int intif_guild_castle_dataload(int num, int *castle_ids);
 int intif_guild_castle_datasave(int castle_id, int index, int value);
 #ifdef BOUND_ITEMS
-void intif_itembound_req(uint32 char_id, uint32 aid, int guild_id);
+void intif_itembound_guild_retrieve(uint32 char_id, uint32 account_id, int guild_id);
 #endif
 
 int intif_create_pet(uint32 account_id, uint32 char_id, short pet_type, short pet_lv, short pet_egg_id,

+ 2 - 3
src/map/itemdb.c

@@ -419,10 +419,9 @@ struct item_data* itemdb_search(unsigned short nameid) {
 * @param id Item data
 * @return True if item is equip, false otherwise
 */
-bool itemdb_isequip2(struct item_data *id)
-{
+bool itemdb_isequip2(struct item_data *id) {
 	nullpo_ret(id);
-	switch(id->type) {
+	switch (id->type) {
 		case IT_WEAPON:
 		case IT_ARMOR:
 		case IT_AMMO:

+ 1 - 0
src/map/log.c

@@ -74,6 +74,7 @@ static char log_picktype2char(e_log_pick_type type)
 		case LOG_TYPE_BANK:             return 'K';  // Ban(K) Transactions
 		case LOG_TYPE_OTHER:			return 'X';  // Other
 		case LOG_TYPE_CASH:				return '$';  // Cash
+		case LOG_TYPE_BOUND_REMOVAL:	return 'B';  // Removed bound items when guild/party is broken
 	}
 
 	// should not get here, fallback

+ 1 - 0
src/map/log.h

@@ -43,6 +43,7 @@ typedef enum e_log_pick_type
 	LOG_TYPE_OTHER            = 0x10000,
 	LOG_TYPE_CASH             = 0x20000,
 	LOG_TYPE_BANK             = 0x40000,
+	LOG_TYPE_BOUND_REMOVAL    = 0x80000,
 	// combinations
 	LOG_TYPE_LOOT             = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME,
 	// all

+ 10 - 10
src/map/map.c

@@ -1528,16 +1528,16 @@ bool map_closest_freecell(int16 m, int16 *x, int16 *y, int type, int flag)
 int map_addflooritem(struct item *item,int amount,int16 m,int16 x,int16 y,int first_charid,int second_charid,int third_charid,int flags)
 {
 	int r;
-	struct flooritem_data *fitem=NULL;
+	struct flooritem_data *fitem = NULL;
 
 	nullpo_ret(item);
 
-	if(!(flags&4) && battle_config.item_onfloor && (itemdb_traderight(item->nameid)&1) )
+	if (!(flags&4) && battle_config.item_onfloor && (itemdb_traderight(item->nameid)&1))
 		return 0; //can't be dropped
 
-	if(!map_searchrandfreecell(m,&x,&y,flags&2?1:0))
+	if (!map_searchrandfreecell(m,&x,&y,flags&2?1:0))
 		return 0;
-	r=rnd();
+	r = rnd();
 
 	CREATE(fitem, struct flooritem_data, 1);
 	fitem->bl.type=BL_ITEM;
@@ -1546,7 +1546,7 @@ int map_addflooritem(struct item *item,int amount,int16 m,int16 x,int16 y,int fi
 	fitem->bl.x=x;
 	fitem->bl.y=y;
 	fitem->bl.id = map_get_new_object_id();
-	if(fitem->bl.id==0){
+	if (fitem->bl.id==0) {
 		aFree(fitem);
 		return 0;
 	}
@@ -1559,13 +1559,13 @@ int map_addflooritem(struct item *item,int amount,int16 m,int16 x,int16 y,int fi
 	fitem->third_get_tick = fitem->second_get_tick + (flags&1 ? battle_config.mvp_item_third_get_time : battle_config.item_third_get_time);
 
 	memcpy(&fitem->item,item,sizeof(*item));
-	fitem->item.amount=amount;
-	fitem->subx=(r&3)*3+3;
-	fitem->suby=((r>>2)&3)*3+3;
-	fitem->cleartimer=add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0);
+	fitem->item.amount = amount;
+	fitem->subx = (r&3)*3+3;
+	fitem->suby = ((r>>2)&3)*3+3;
+	fitem->cleartimer = add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0);
 
 	map_addiddb(&fitem->bl);
-	if(map_addblock(&fitem->bl))
+	if (map_addblock(&fitem->bl))
 		return 0;
 	clif_dropflooritem(fitem);
 

+ 27 - 7
src/map/party.c

@@ -17,6 +17,7 @@
 #include "instance.h"
 #include "intif.h"
 #include "mapreg.h"
+#include "trade.h"
 
 #include <stdlib.h>
 
@@ -188,6 +189,20 @@ int party_request_info(int party_id, uint32 char_id)
 	return intif_request_partyinfo(party_id, char_id);
 }
 
+/**
+ * Close trade window if party member is kicked when trade a party bound item
+ * @param sd
+ **/
+static void party_trade_bound_cancel(struct map_session_data *sd) {
+#ifdef BOUND_ITEMS
+	nullpo_retv(sd);
+	if (sd->state.isBoundTrading&(1<<BOUND_PARTY))
+		trade_tradecancel(sd);
+#else
+	;
+#endif
+}
+
 /// Invoked (from char-server) when the party info is not found.
 int party_recv_noinfo(int party_id, uint32 char_id)
 {
@@ -534,6 +549,7 @@ int party_removemember(struct map_session_data* sd, uint32 account_id, char* nam
 	if( i == MAX_PARTY )
 		return 0; // no such char in party
 
+	party_trade_bound_cancel(sd);
 	intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id);
 
 	return 1;
@@ -544,6 +560,8 @@ int party_removemember2(struct map_session_data *sd,uint32 char_id,int party_id)
 	if( sd ) {
 		if( !sd->status.party_id )
 			return -3;
+
+		party_trade_bound_cancel(sd);
 		intif_party_leave(sd->status.party_id,sd->status.account_id,sd->status.char_id);
 		return 1;
 	} else {
@@ -576,6 +594,7 @@ int party_leave(struct map_session_data *sd)
 	if( i == MAX_PARTY )
 		return 0;
 
+	party_trade_bound_cancel(sd);
 	intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id);
 	return 1;
 }
@@ -603,10 +622,11 @@ int party_member_withdraw(int party_id, uint32 account_id, uint32 char_id)
 		int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion
 		int j,i;
 
+		party_trade_bound_cancel(sd);
 		j = pc_bound_chk(sd,BOUND_PARTY,idxlist);
 
 		for(i = 0; i < j; i++)
-			pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER);
+			pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_BOUND_REMOVAL);
 #endif
 
 		sd->status.party_id = 0;
@@ -1060,7 +1080,7 @@ int party_exp_share(struct party_data* p, struct block_list* src, unsigned int b
 }
 
 //Does party loot. first_charid holds the charid of the player who has time priority to take the item.
-int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item_data, int first_charid)
+int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item, int first_charid)
 {
 	TBL_PC* target = NULL;
 	int i;
@@ -1080,7 +1100,7 @@ int party_share_loot(struct party_data* p, struct map_session_data* sd, struct i
 				if( (psd = p->data[i].sd) == NULL || sd->bl.m != psd->bl.m || pc_isdead(psd) || (battle_config.idle_no_share && pc_isidle(psd)) )
 					continue;
 
-				if (pc_additem(psd,item_data,item_data->amount,LOG_TYPE_PICKDROP_PLAYER))
+				if (pc_additem(psd,item,item->amount,LOG_TYPE_PICKDROP_PLAYER))
 					continue; //Chosen char can't pick up loot.
 
 				//Successful pick.
@@ -1103,7 +1123,7 @@ int party_share_loot(struct party_data* p, struct map_session_data* sd, struct i
 			while (count > 0) { //Pick a random member.
 				i = rnd()%count;
 
-				if (pc_additem(psd[i],item_data,item_data->amount,LOG_TYPE_PICKDROP_PLAYER)) { // Discard this receiver.
+				if (pc_additem(psd[i],item,item->amount,LOG_TYPE_PICKDROP_PLAYER)) { // Discard this receiver.
 					psd[i] = psd[count-1];
 					count--;
 				} else { // Successful pick.
@@ -1117,12 +1137,12 @@ int party_share_loot(struct party_data* p, struct map_session_data* sd, struct i
 	if (!target) {
 		target = sd; //Give it to the char that picked it up
 
-		if ((i = pc_additem(sd,item_data,item_data->amount,LOG_TYPE_PICKDROP_PLAYER)))
+		if ((i = pc_additem(sd,item,item->amount,LOG_TYPE_PICKDROP_PLAYER)))
 			return i;
 	}
 
-	if( p && battle_config.party_show_share_picker && battle_config.show_picker_item_type&(1<<itemdb_type(item_data->nameid)) )
-		clif_party_show_picker(target, item_data);
+	if( p && battle_config.party_show_share_picker && battle_config.show_picker_item_type&(1<<itemdb_type(item->nameid)) )
+		clif_party_show_picker(target, item);
 
 	return 0;
 }

+ 1 - 1
src/map/party.h

@@ -84,7 +84,7 @@ int party_recv_message(int party_id,uint32 account_id,const char *mes,int len);
 int party_skill_check(struct map_session_data *sd, int party_id, uint16 skill_id, uint16 skill_lv);
 int party_send_xy_clear(struct party_data *p);
 int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny);
-int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item_data, int first_charid);
+int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item, int first_charid);
 int party_send_dot_remove(struct map_session_data *sd);
 int party_sub_count(struct block_list *bl, va_list ap);
 int party_sub_count_class(struct block_list *bl, va_list ap);

+ 13 - 13
src/map/pc.c

@@ -4430,47 +4430,47 @@ bool pc_dropitem(struct map_session_data *sd,int n,int amount)
  *------------------------------------------*/
 bool pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem)
 {
-	int flag=0;
+	int flag = 0;
 	unsigned int tick = gettick();
 	struct party_data *p = NULL;
 
 	nullpo_ret(sd);
 	nullpo_ret(fitem);
 
-	if(!check_distance_bl(&fitem->bl, &sd->bl, 2) && sd->ud.skill_id!=BS_GREED)
+	if (!check_distance_bl(&fitem->bl, &sd->bl, 2) && sd->ud.skill_id!=BS_GREED)
 		return false;	// Distance is too far
 
-	if( sd->sc.cant.pickup )
+	if (sd->sc.cant.pickup)
 		return false;
 
 	if (sd->status.party_id)
 		p = party_search(sd->status.party_id);
 
-	if(fitem->first_get_charid > 0 && fitem->first_get_charid != sd->status.char_id) {
+	if (fitem->first_get_charid > 0 && fitem->first_get_charid != sd->status.char_id) {
 		struct map_session_data *first_sd = map_charid2sd(fitem->first_get_charid);
-		if(DIFF_TICK(tick,fitem->first_get_tick) < 0) {
+		if (DIFF_TICK(tick,fitem->first_get_tick) < 0) {
 			if (!(p && p->party.item&1 &&
 				first_sd && first_sd->status.party_id == sd->status.party_id
-			))
+				))
 				return false;
 		}
-		else if(fitem->second_get_charid > 0 && fitem->second_get_charid != sd->status.char_id) {
+		else if (fitem->second_get_charid > 0 && fitem->second_get_charid != sd->status.char_id) {
 			struct map_session_data *second_sd = map_charid2sd(fitem->second_get_charid);
-			if(DIFF_TICK(tick, fitem->second_get_tick) < 0) {
-				if(!(p && p->party.item&1 &&
+			if (DIFF_TICK(tick, fitem->second_get_tick) < 0) {
+				if (!(p && p->party.item&1 &&
 					((first_sd && first_sd->status.party_id == sd->status.party_id) ||
 					(second_sd && second_sd->status.party_id == sd->status.party_id))
-				))
+					))
 					return false;
 			}
-			else if(fitem->third_get_charid > 0 && fitem->third_get_charid != sd->status.char_id){
+			else if (fitem->third_get_charid > 0 && fitem->third_get_charid != sd->status.char_id){
 				struct map_session_data *third_sd = map_charid2sd(fitem->third_get_charid);
-				if(DIFF_TICK(tick,fitem->third_get_tick) < 0) {
+				if (DIFF_TICK(tick,fitem->third_get_tick) < 0) {
 					if(!(p && p->party.item&1 &&
 						((first_sd && first_sd->status.party_id == sd->status.party_id) ||
 						(second_sd && second_sd->status.party_id == sd->status.party_id) ||
 						(third_sd && third_sd->status.party_id == sd->status.party_id))
-					))
+						))
 						return false;
 				}
 			}

+ 1 - 0
src/map/pc.h

@@ -229,6 +229,7 @@ struct map_session_data {
 		unsigned int banking : 1; //1 when we using the banking system 0 when closed
 		unsigned int hpmeter_visible : 1;
 		unsigned disable_atcommand_on_npc : 1; //Prevent to use atcommand while talking with NPC [Kichi]
+		uint8 isBoundTrading; // Player is currently add bound item to trade list [Cydh]
 	} state;
 	struct {
 		unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;

+ 1 - 1
src/map/script.c

@@ -9232,7 +9232,7 @@ BUILDIN_FUNC(guildopenstorage)
 	if( sd == NULL )
 		return 0;
 
-	ret = storage_guild_storageopen(sd);
+	ret = gstorage_storageopen(sd);
 	script_pushint(st,ret);
 	return SCRIPT_CMD_SUCCESS;
 }

+ 141 - 113
src/map/storage.c

@@ -5,6 +5,7 @@
 #include "../common/db.h"
 #include "../common/nullpo.h"
 #include "../common/malloc.h"
+#include "../common/showmsg.h"
 
 #include "map.h" // struct map_session_data
 #include "storage.h"
@@ -85,8 +86,8 @@ static int storage_reconnect_sub(DBKey key, DBData *data, va_list ap)
 {
 	struct guild_storage *stor = db_data2ptr(data);
 
-	if (stor->dirty && stor->storage_status == 0) //Save closed storages.
-		storage_guild_storagesave(0, stor->guild_id,0);
+	if (stor->dirty && stor->opened == 0) //Save closed storages.
+		gstorage_storagesave(0, stor->guild_id,0);
 
 	return 0;
 }
@@ -421,12 +422,12 @@ static DBData create_guildstorage(DBKey key, va_list args)
  * @param guild_id : id of the guild
  * @return guild_storage
  */
-struct guild_storage *guild2storage(int guild_id)
+struct guild_storage *gstorage_guild2storage(int guild_id)
 {
 	struct guild_storage *gs = NULL;
 
-	if(guild_search(guild_id) != NULL)
-		gs = idb_ensure(guild_storage_db,guild_id,create_guildstorage);
+	if (guild_search(guild_id) != NULL)
+		gs = (struct guild_storage *)idb_ensure(guild_storage_db,guild_id,create_guildstorage);
 
 	return gs;
 }
@@ -437,7 +438,7 @@ struct guild_storage *guild2storage(int guild_id)
  * @param guild_id : guild_id to search the storage
  * @return guild_storage or NULL
  */
-struct guild_storage *guild2storage2(int guild_id)
+struct guild_storage *gstorage_get_storage(int guild_id)
 {
 	return (struct guild_storage*)idb_get(guild_storage_db,guild_id);
 }
@@ -447,11 +448,9 @@ struct guild_storage *guild2storage2(int guild_id)
  * @param guild_id : guild to remove the storage from
  * @return 0
  */
-int guild_storage_delete(int guild_id)
+void gstorage_delete(int guild_id)
 {
 	idb_remove(guild_storage_db,guild_id);
-
-	return 0;
 }
 
 /**
@@ -459,7 +458,7 @@ int guild_storage_delete(int guild_id)
  * @param sd : player
  * @return 0 : success, 1 : fail, 2 : no guild found
  */
-int storage_guild_storageopen(struct map_session_data* sd)
+char gstorage_storageopen(struct map_session_data* sd)
 {
 	struct guild_storage *gstor;
 
@@ -476,18 +475,18 @@ int storage_guild_storageopen(struct map_session_data* sd)
 		return 1;
 	}
 
-	if((gstor = guild2storage2(sd->status.guild_id)) == NULL) {
+	if((gstor = gstorage_get_storage(sd->status.guild_id)) == NULL) {
 		intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
 		return 0;
 	}
 
-	if(gstor->storage_status)
+	if(gstor->opened)
 		return 1;
 
-	if( gstor->lock )
+	if( gstor->locked )
 		return 1;
 
-	gstor->storage_status = 1;
+	gstor->opened = sd->status.char_id;
 	sd->state.storage_flag = 2;
 	storage_sortitem(gstor->items, ARRAYLENGTH(gstor->items));
 	clif_storagelist(sd, gstor->items, ARRAYLENGTH(gstor->items));
@@ -500,66 +499,109 @@ int storage_guild_storageopen(struct map_session_data* sd)
  * 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_data : item to add
+ * @param item : item to add
  * @param amount : number of item to add
- * @return 0 : success, 1 : fail
+ * @return True : success, False : fail
  */
-char guild_storage_additem(struct map_session_data* sd, struct guild_storage* stor, struct item* item_data, int amount)
+bool gstorage_additem(struct map_session_data* sd, struct guild_storage* stor, struct item* item, int amount)
 {
-	struct item_data *data;
+	struct item_data *id;
 	int i;
 
-	nullpo_retr(1, sd);
-	nullpo_retr(1, stor);
-	nullpo_retr(1, item_data);
+	nullpo_ret(sd);
+	nullpo_ret(stor);
+	nullpo_ret(item);
 
-	if(item_data->nameid == 0 || amount <= 0)
-		return 1;
+	if(item->nameid == 0 || amount <= 0)
+		return false;
 
-	data = itemdb_search(item_data->nameid);
+	id = itemdb_search(item->nameid);
 
-	if( data->stack.guildstorage && amount > data->stack.amount ) // item stack limitation
-		return 1;
+	if( id->stack.guildstorage && 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]
+	if( !itemdb_canguildstore(item, pc_get_group_level(sd)) || item->expire_time ) { // Check if item is storable. [Skotlex]
 		clif_displaymessage (sd->fd, msg_txt(sd,264));
-		return 1;
+		return false;
 	}
 
-	if( (item_data->bound == BOUND_ACCOUNT || item_data->bound > BOUND_GUILD) && !pc_can_give_bounded_items(sd) ) {
+	if( (item->bound == BOUND_ACCOUNT || item->bound > BOUND_GUILD) && !pc_can_give_bounded_items(sd) ) {
 		clif_displaymessage(sd->fd, msg_txt(sd,294));
-		return 1;
+		return false;
 	}
 
-	if(itemdb_isstackable2(data)) { //Stackable
+	if(itemdb_isstackable2(id)) { //Stackable
 		for(i = 0; i < MAX_GUILD_STORAGE; i++){
-			if(compare_item(&stor->items[i], item_data)) {
-				if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.guildstorage && amount > data->stack.amount - stor->items[i].amount ) )
-					return 1;
+			if(compare_item(&stor->items[i], item)) {
+				if( amount > MAX_AMOUNT - stor->items[i].amount || ( id->stack.guildstorage && amount > id->stack.amount - stor->items[i].amount ) )
+					return false;
 	
 				stor->items[i].amount+=amount;
 				clif_storageitemadded(sd,&stor->items[i],i,amount);
-				stor->dirty = 1;
-
-				return 0;
+				stor->dirty = true;
+				return true;
 			}
 		}
 	}
 
 	//Add item
 	for(i = 0; i < MAX_GUILD_STORAGE && stor->items[i].nameid; i++);
-
 	if(i >= MAX_GUILD_STORAGE)
-		return 1;
+		return false;
 
-	memcpy(&stor->items[i],item_data,sizeof(stor->items[0]));
-	stor->items[i].amount=amount;
+	memcpy(&stor->items[i],item,sizeof(stor->items[0]));
+	stor->items[i].amount = amount;
 	stor->storage_amount++;
 	clif_storageitemadded(sd,&stor->items[i],i,amount);
 	clif_updatestorageamount(sd, stor->storage_amount, MAX_GUILD_STORAGE);
-	stor->dirty = 1;
+	stor->dirty = true;
+	return true;
+}
 
-	return 0;
+/**
+ * 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 gstorage_additem2(struct guild_storage* stor, struct item* item, int amount) {
+	struct item_data *id;
+	int i;
+
+	nullpo_ret(stor);
+	nullpo_ret(item);
+
+	if (item->nameid == 0 || amount <= 0 || !(id = itemdb_exists(item->nameid)))
+		return false;
+
+	if (item->expire_time)
+		return false;
+
+	if (itemdb_isstackable2(id)) { // Stackable
+		for (i = 0; i < MAX_GUILD_STORAGE; i++) {
+			if (compare_item(&stor->items[i], item)) {
+				// Set the amount, make it fit with max amount
+				amount = min(amount, ((id->stack.guildstorage) ? id->stack.amount : MAX_AMOUNT) - stor->items[i].amount);
+				if (amount != item->amount)
+					ShowWarning("gstorage_additem2: Stack limit reached! Altered amount of item \""CL_WHITE"%s"CL_RESET"\" (%d). '"CL_WHITE"%d"CL_RESET"' -> '"CL_WHITE"%d"CL_RESET"'.\n", id->name, id->nameid, item->amount, amount);
+				stor->items[i].amount += amount;
+				stor->dirty = true;
+				return true;
+			}
+		}
+	}
+
+	// Add the item
+	for (i = 0; i < MAX_GUILD_STORAGE && stor->items[i].nameid; i++);
+	if (i >= MAX_GUILD_STORAGE)
+		return false;
+
+	memcpy(&stor->items[i], item, sizeof(stor->items[0]));
+	stor->items[i].amount = amount;
+	stor->storage_amount++;
+	stor->dirty = true;
+	return true;
 }
 
 /**
@@ -568,15 +610,15 @@ char guild_storage_additem(struct map_session_data* sd, struct guild_storage* st
  * @param stor : guild_storage
  * @param n : index of item in guild storage
  * @param amount : number of item to delete
- * @return 0 : success, 1 : fail
+ * @return True : success, False : fail
  */
-int guild_storage_delitem(struct map_session_data* sd, struct guild_storage* stor, int n, int amount)
+bool gstorage_delitem(struct map_session_data* sd, struct guild_storage* stor, int n, int amount)
 {
 	nullpo_retr(1, sd);
 	nullpo_retr(1, stor);
 
 	if(stor->items[n].nameid == 0 || stor->items[n].amount < amount)
-		return 1;
+		return false;
 
 	stor->items[n].amount -= amount;
 
@@ -587,25 +629,23 @@ int guild_storage_delitem(struct map_session_data* sd, struct guild_storage* sto
 	}
 
 	clif_storageitemremoved(sd,n,amount);
-	stor->dirty = 1;
-
-	return 0;
+	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
- * @return 1:success, 0:fail
  */
-void storage_guild_storageadd(struct map_session_data* sd, int index, int amount)
+void gstorage_storageadd(struct map_session_data* sd, int index, int amount)
 {
 	struct guild_storage *stor;
 
 	nullpo_retv(sd);
-	nullpo_retv(stor = guild2storage2(sd->status.guild_id));
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
-	if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE )
+	if( !stor->opened || stor->opened != sd->status.char_id || stor->storage_amount > MAX_GUILD_STORAGE )
 		return;
 
 	if( index < 0 || index >= MAX_INVENTORY )
@@ -617,12 +657,12 @@ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount
 	if( amount < 1 || amount > sd->status.inventory[index].amount )
 		return;
 
-	if( stor->lock ) {
-		storage_guild_storageclose(sd);
+	if( stor->locked ) {
+		gstorage_storageclose(sd);
 		return;
 	}
 
-	if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount) == 0)
+	if(gstorage_additem(sd,stor,&sd->status.inventory[index],amount))
 		pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE);
 	else {
 		clif_storageitemremoved(sd,index,0);
@@ -637,16 +677,16 @@ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount
  * @param amount : number of item to get
  * @return 1:success, 0:fail
  */
-void storage_guild_storageget(struct map_session_data* sd, int index, int amount)
+void gstorage_storageget(struct map_session_data* sd, int index, int amount)
 {
 	struct guild_storage *stor;
 	unsigned char flag = 0;
 
 	nullpo_retv(sd);
-	nullpo_retv(stor = guild2storage2(sd->status.guild_id));
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
-	if(!stor->storage_status)
-  		return;
+	if(!stor->opened || stor->opened != sd->status.char_id)
+		return;
 
 	if(index < 0 || index >= MAX_GUILD_STORAGE)
 		return;
@@ -655,15 +695,15 @@ void storage_guild_storageget(struct map_session_data* sd, int index, int amount
 		return;
 
 	if(amount < 1 || amount > stor->items[index].amount)
-	  	return;
+		return;
 
-	if( stor->lock ) {
-		storage_guild_storageclose(sd);
+	if( stor->locked ) {
+		gstorage_storageclose(sd);
 		return;
 	}
 
 	if((flag = pc_additem(sd,&stor->items[index],amount,LOG_TYPE_GSTORAGE)) == 0)
-		guild_storage_delitem(sd,stor,index,amount);
+		gstorage_delitem(sd,stor,index,amount);
 	else { // inform fail
 		clif_storageitemremoved(sd,index,0);
 		clif_additem(sd,0,0,flag);
@@ -675,16 +715,15 @@ void storage_guild_storageget(struct map_session_data* sd, int index, int amount
  * @param sd : player
  * @param index : index of item in cart
  * @param amount : number of item to transfer
- * @return 1:fail, 0:success
  */
-void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, int amount)
+void gstorage_storageaddfromcart(struct map_session_data* sd, int index, int amount)
 {
 	struct guild_storage *stor;
 
 	nullpo_retv(sd);
-	nullpo_retv(stor = guild2storage2(sd->status.guild_id));
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
-	if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE )
+	if( !stor->opened || stor->opened != sd->status.char_id || stor->storage_amount > MAX_GUILD_STORAGE )
 		return;
 
 	if( index < 0 || index >= MAX_CART )
@@ -696,7 +735,7 @@ void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, in
 	if( amount < 1 || amount > sd->status.cart[index].amount )
 		return;
 
-	if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount) == 0)
+	if(gstorage_additem(sd,stor,&sd->status.cart[index],amount))
 		pc_cart_delitem(sd,index,amount,0,LOG_TYPE_GSTORAGE);
 	else {
 		clif_storageitemremoved(sd,index,0);
@@ -711,19 +750,19 @@ void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, in
  * @param amount : number of item to transfer
  * @return 1:fail, 0:success
  */
-void storage_guild_storagegettocart(struct map_session_data* sd, int index, int amount)
+void gstorage_storagegettocart(struct map_session_data* sd, int index, int amount)
 {
 	short flag;
 	struct guild_storage *stor;
 
 	nullpo_retv(sd);
-	nullpo_retv(stor = guild2storage2(sd->status.guild_id));
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
-	if(!stor->storage_status)
-	  	return;
+	if(!stor->opened || stor->opened != sd->status.char_id)
+		return;
 
 	if(index < 0 || index >= MAX_GUILD_STORAGE)
-	  	return;
+		return;
 
 	if(stor->items[index].nameid == 0)
 		return;
@@ -732,7 +771,7 @@ void storage_guild_storagegettocart(struct map_session_data* sd, int index, int
 		return;
 
 	if((flag = pc_cart_additem(sd,&stor->items[index],amount,LOG_TYPE_GSTORAGE)) == 0)
-		guild_storage_delitem(sd,stor,index,amount);
+		gstorage_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);
@@ -744,104 +783,93 @@ void storage_guild_storagegettocart(struct map_session_data* sd, int index, int
  * @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 0 : fail (no storage), 1 : success (requested)
+ * @return False : fail (no storage), True : success (requested)
  */
-int storage_guild_storagesave(uint32 account_id, int guild_id, int flag)
+bool gstorage_storagesave(uint32 account_id, int guild_id, int flag)
 {
-	struct guild_storage *stor = guild2storage2(guild_id);
+	struct guild_storage *stor = gstorage_get_storage(guild_id);
 
-	if(stor) {
+	if (stor) {
 		if (flag) //Char quitting, close it.
-			stor->storage_status = 0;
+			stor->opened = 0;
 
-	 	if (stor->dirty)
+		if (stor->dirty)
 			intif_send_guild_storage(account_id,stor);
 
-		return 1;
+		return true;
 	}
 
-	return 0;
+	return false;
 }
 
 /**
  * ACK save of guild storage
  * @param guild_id : guild to use the storage
- * @return 0 : fail (no storage), 1 : success
  */
-int storage_guild_storagesaved(int guild_id)
+void gstorage_storagesaved(int guild_id)
 {
 	struct guild_storage *stor;
 
-	if((stor = guild2storage2(guild_id)) != NULL) {
-		if (stor->dirty && stor->storage_status == 0) // Storage has been correctly saved.
-			stor->dirty = 0;
-
-		return 1;
+	if ((stor = gstorage_get_storage(guild_id)) != NULL) {
+		if (stor->dirty && stor->opened == 0) // Storage has been correctly saved.
+			stor->dirty = false;
 	}
-
-	return 0;
 }
 
 /**
  * Close storage for player then save it
  * @param sd : player
- * @return 0
  */
-int storage_guild_storageclose(struct map_session_data* sd)
+void gstorage_storageclose(struct map_session_data* sd)
 {
 	struct guild_storage *stor;
 
-	nullpo_ret(sd);
-	nullpo_ret(stor = guild2storage2(sd->status.guild_id));
+	nullpo_retv(sd);
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
 	clif_storageclose(sd);
-	if (stor->storage_status) {
+	if (stor->opened) {
 		if (save_settings&CHARSAVE_STORAGE)
 			chrif_save(sd, 0); //This one also saves the storage. [Skotlex]
 		else
-			storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0);
+			gstorage_storagesave(sd->status.account_id, sd->status.guild_id,0);
 
-		stor->storage_status=0;
+		stor->opened = 0;
 	}
 
 	sd->state.storage_flag = 0;
-
-	return 0;
 }
 
 /**
  * Close storage for player then save it
  * @param sd
  * @param flag
- * @return 
  */
-int storage_guild_storage_quit(struct map_session_data* sd, int flag)
+void gstorage_storage_quit(struct map_session_data* sd, int flag)
 {
 	struct guild_storage *stor;
 
-	nullpo_ret(sd);
-	nullpo_ret(stor=guild2storage2(sd->status.guild_id));
+	nullpo_retv(sd);
+	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));
 
-	if(flag) { // Only during a guild break flag is 1 (don't save storage)
+	if (flag) { // Only during a guild break flag is 1 (don't save storage)
 		sd->state.storage_flag = 0;
-		stor->storage_status = 0;
+		stor->opened = 0;
 		clif_storageclose(sd);
 
 		if (save_settings&CHARSAVE_STORAGE)
 			chrif_save(sd,0);
 
-		return 0;
+		return;
 	}
 
-	if(stor->storage_status) {
+	if (stor->opened) {
 		if (save_settings&CHARSAVE_STORAGE)
 			chrif_save(sd,0);
 		else
-			storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1);
+			gstorage_storagesave(sd->status.account_id,sd->status.guild_id,1);
 	}
 
 	sd->state.storage_flag = 0;
-	stor->storage_status = 0;
-
-	return 0;
+	stor->opened = 0;
 }

+ 15 - 14
src/map/storage.h

@@ -24,20 +24,21 @@ void do_final_storage(void);
 void do_reconnect_storage(void);
 void storage_storage_quit(struct map_session_data *sd, int flag);
 
-struct guild_storage* guild2storage(int guild_id);
-struct guild_storage *guild2storage2(int guild_id);
-int guild_storage_delete(int guild_id);
-int storage_guild_storageopen(struct map_session_data *sd);
-char guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount);
-int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
-void storage_guild_storageadd(struct map_session_data *sd,int index,int amount);
-void storage_guild_storageget(struct map_session_data *sd,int index,int amount);
-void storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount);
-void storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount);
-int storage_guild_storageclose(struct map_session_data *sd);
-int storage_guild_storage_quit(struct map_session_data *sd,int flag);
-int storage_guild_storagesave(uint32 account_id, int guild_id, int flag);
-int storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved.
+struct guild_storage* gstorage_guild2storage(int guild_id);
+struct guild_storage *gstorage_get_storage(int guild_id);
+void gstorage_delete(int guild_id);
+char gstorage_storageopen(struct map_session_data *sd);
+bool gstorage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item,int amount);
+bool gstorage_additem2(struct guild_storage *stor, struct item *item, int amount);
+bool gstorage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
+void gstorage_storageadd(struct map_session_data *sd,int index,int amount);
+void gstorage_storageget(struct map_session_data *sd,int index,int amount);
+void gstorage_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+void gstorage_storagegettocart(struct map_session_data *sd,int index,int amount);
+void gstorage_storageclose(struct map_session_data *sd);
+void gstorage_storage_quit(struct map_session_data *sd,int flag);
+bool gstorage_storagesave(uint32 account_id, int guild_id, int flag);
+void gstorage_storagesaved(int guild_id);
 
 int compare_item(struct item *a, struct item *b);
 

+ 10 - 0
src/map/trade.c

@@ -391,6 +391,9 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount)
 		return;
 	}
 
+	if (item->bound)
+		sd->state.isBoundTrading |= (1<<item->bound);
+
 	// Locate a trade position
 	ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 );
 	if( trade_i == 10 ) { // No space left
@@ -483,7 +486,10 @@ void trade_tradecancel(struct map_session_data *sd)
 	struct map_session_data *target_sd;
 	int trade_i;
 
+	nullpo_retv(sd);
+
 	target_sd = map_id2sd(sd->trade_partner);
+	sd->state.isBoundTrading = 0;
 
 	if(!sd->state.trading) { // Not trade accepted
 		if( target_sd ) {
@@ -546,6 +552,8 @@ void trade_tradecommit(struct map_session_data *sd)
 	struct map_session_data *tsd;
 	int trade_i;
 
+	nullpo_retv(sd);
+
 	if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade.
 		return;
 
@@ -624,10 +632,12 @@ void trade_tradecommit(struct map_session_data *sd)
 	sd->state.deal_locked = 0;
 	sd->trade_partner = 0;
 	sd->state.trading = 0;
+	sd->state.isBoundTrading = 0;
 
 	tsd->state.deal_locked = 0;
 	tsd->trade_partner = 0;
 	tsd->state.trading = 0;
+	tsd->state.isBoundTrading = 0;
 
 	clif_tradecompleted(sd, 0);
 	clif_tradecompleted(tsd, 0);

+ 1 - 1
src/map/unit.c

@@ -2855,7 +2855,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 				if (sd->state.storage_flag == 1)
 					storage_storage_quit(sd,0);
 				else if (sd->state.storage_flag == 2)
-					storage_guild_storage_quit(sd,0);
+					gstorage_storage_quit(sd,0);
 
 				sd->state.storage_flag = 0; //Force close it when being warped.
 			}