Browse Source

* Changed how the Unique ID System saves and creates IDs for items. (Hercules dd49dbc)
- System is now enabled by default.
- All non-stackable items that previously existed will get unique IDs if items::item_check is enabled.
- Added 'getequipuniqueid' script command to get the unique ID of an equipment on a player.
- Don't forget to apply the SQL update!
* Renamed SQL update file that applied to logs.

aleos89 10 years ago
parent
commit
c0ed836e87

+ 9 - 1
doc/script_commands.txt

@@ -2540,7 +2540,15 @@ armor, but also don't want them to equip if after the check, you would do this:
 
 
 ---------------------------------------
 ---------------------------------------
 
 
-*getequipname(<equpment slot>)
+*getequipuniqueid(<equipment slot>)
+
+This function returns the unique ID (as a string) of the item equipped in the equipment slot 
+specified on the invoking character. If nothing is equipped there, it returns an empty string.
+See 'getequipid' for a full list of valid equipment slots.
+
+---------------------------------------
+
+*getequipname(<equipment slot>)
 
 
 Returns the jname of the item equipped in the specified equipment slot on the
 Returns the jname of the item equipped in the specified equipment slot on the
 invoking character, or an empty string if nothing is equipped in that position.
 invoking character, or an empty string if nothing is equipped in that position.

+ 1 - 2
sql-files/main.sql

@@ -124,6 +124,7 @@ CREATE TABLE IF NOT EXISTS `char` (
   `moves` int(11) unsigned NOT NULL DEFAULT '0',
   `moves` int(11) unsigned NOT NULL DEFAULT '0',
   `unban_time` int(11) unsigned NOT NULL default '0',
   `unban_time` int(11) unsigned NOT NULL default '0',
   `font` tinyint(3) unsigned NOT NULL default '0',
   `font` tinyint(3) unsigned NOT NULL default '0',
+  `uniqueitem_counter` bigint(20) NOT NULL,
   PRIMARY KEY  (`char_id`),
   PRIMARY KEY  (`char_id`),
   UNIQUE KEY `name_key` (`name`),
   UNIQUE KEY `name_key` (`name`),
   KEY `account_id` (`account_id`),
   KEY `account_id` (`account_id`),
@@ -698,8 +699,6 @@ CREATE TABLE IF NOT EXISTS `interreg` (
   `value` varchar(20) NOT NULL,
   `value` varchar(20) NOT NULL,
    PRIMARY KEY (`varname`)
    PRIMARY KEY (`varname`)
 ) ENGINE=InnoDB;
 ) ENGINE=InnoDB;
-INSERT INTO `interreg` (`varname`, `value`) VALUES
-('unique_id', '0');
 
 
 --
 --
 -- Table structure for table `bonus_script`
 -- Table structure for table `bonus_script`

+ 0 - 0
sql-files/upgrades/upgrade_20140713.sql → sql-files/upgrades/upgrade_20140713_log.sql


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

@@ -0,0 +1 @@
+ALTER TABLE `char` ADD COLUMN `uniqueitem_counter` bigint(20) NOT NULL AFTER `font`;

+ 5 - 11
src/char/char.c

@@ -1,7 +1,6 @@
 // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
 // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
 // For more information, see LICENCE in the main folder
 // For more information, see LICENCE in the main folder
 
 
-
 #include <time.h>
 #include <time.h>
 #include <signal.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdarg.h>
@@ -335,7 +334,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p){
 		(p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
 		(p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
 		(p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) ||
 		(p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) ||
 		(p->rename != cp->rename) || (p->robe != cp->robe) || (p->character_moves != cp->character_moves) ||
 		(p->rename != cp->rename) || (p->robe != cp->robe) || (p->character_moves != cp->character_moves) ||
-		(p->unban_time != cp->unban_time) || (p->font != cp->font)
+		(p->unban_time != cp->unban_time) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter)
 	)
 	)
 	{	//Save status
 	{	//Save status
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
@@ -345,7 +344,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p){
 			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
 			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
 			"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
 			"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
 			"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d',"
 			"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d',"
-			"`delete_date`='%lu',`robe`='%d',`moves`='%d',`font`='%u'"
+			"`delete_date`='%lu',`robe`='%d',`moves`='%d',`font`='%u',`uniqueitem_counter`='%u'"
 			" WHERE `account_id`='%d' AND `char_id` = '%d'",
 			" WHERE `account_id`='%d' AND `char_id` = '%d'",
 			schema_config.char_db, p->base_level, p->job_level,
 			schema_config.char_db, p->base_level, p->job_level,
 			p->base_exp, p->job_exp, p->zeny,
 			p->base_exp, p->job_exp, p->zeny,
@@ -356,7 +355,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p){
 			mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
 			mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
 			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
 			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
 			(unsigned long)p->delete_date, // FIXME: platform-dependent size
 			(unsigned long)p->delete_date, // FIXME: platform-dependent size
-			p->robe,p->character_moves,p->font,
+			p->robe,p->character_moves,p->font, p->uniqueitem_counter,
 			p->account_id, p->char_id) )
 			p->account_id, p->char_id) )
 		{
 		{
 			Sql_ShowDebug(sql_handle);
 			Sql_ShowDebug(sql_handle);
@@ -699,10 +698,7 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl
 		for( j = 0; j < MAX_SLOTS; ++j )
 		for( j = 0; j < MAX_SLOTS; ++j )
 			StringBuf_Printf(&buf, ", '%hu'", items[i].card[j]);
 			StringBuf_Printf(&buf, ", '%hu'", items[i].card[j]);
 		StringBuf_AppendStr(&buf, ")");
 		StringBuf_AppendStr(&buf, ")");
-
-		updateLastUid(items[i].unique_id); // Unique Non Stackable Item ID
 	}
 	}
-	dbUpdateUid(sql_handle); // Unique Non Stackable Item ID
 
 
 	if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
 	if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
 	{
 	{
@@ -840,10 +836,7 @@ int char_inventory_to_sql(const struct item items[], int max, int id) {
 		for( j = 0; j < MAX_SLOTS; ++j )
 		for( j = 0; j < MAX_SLOTS; ++j )
 			StringBuf_Printf(&buf, ", '%hu'", items[i].card[j]);
 			StringBuf_Printf(&buf, ", '%hu'", items[i].card[j]);
 		StringBuf_AppendStr(&buf, ")");
 		StringBuf_AppendStr(&buf, ")");
-
-		updateLastUid(items[i].unique_id);// Unique Non Stackable Item ID
 	}
 	}
-	dbUpdateUid(sql_handle);
 
 
 	if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) {
 	if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) {
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
@@ -989,7 +982,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
 		"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
 		"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
 		"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`,"
 		"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`,"
-		"`unban_time`,`font`"
+		"`unban_time`,`font`,`uniqueitem_counter`"
 		" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
 		" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
 	||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
 	||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
@@ -1048,6 +1041,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_UINT32, &p->character_moves, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_UINT32, &p->character_moves, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_LONG,   &p->unban_time, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_LONG,   &p->unban_time, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_UCHAR,  &p->font, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_UCHAR,  &p->font, 0, NULL, NULL)
+	||  SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UINT,   &p->uniqueitem_counter, 0, NULL, NULL)
 	)
 	)
 	{
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_ShowDebug(stmt);

+ 0 - 4
src/char/int_auction.c

@@ -91,10 +91,6 @@ unsigned int auction_create(struct auction_data *auction)
 		StringBuf_Printf(&buf, ",'%hu'", auction->item.card[j]);
 		StringBuf_Printf(&buf, ",'%hu'", auction->item.card[j]);
 	StringBuf_AppendStr(&buf, ")");
 	StringBuf_AppendStr(&buf, ")");
 
 
-	//Unique Non Stackable Item ID
-	updateLastUid(auction->item.unique_id);
-	dbUpdateUid(sql_handle);
-
 	stmt = SqlStmt_Malloc(sql_handle);
 	stmt = SqlStmt_Malloc(sql_handle);
 	if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
 	if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
 	||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, auction->seller_name, strnlen(auction->seller_name, NAME_LENGTH))
 	||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, auction->seller_name, strnlen(auction->seller_name, NAME_LENGTH))

+ 0 - 4
src/char/int_mail.c

@@ -119,10 +119,6 @@ int mail_savemessage(struct mail_message* msg)
 		StringBuf_Printf(&buf, ", '%hu'", msg->item.card[j]);
 		StringBuf_Printf(&buf, ", '%hu'", msg->item.card[j]);
 	StringBuf_AppendStr(&buf, ")");
 	StringBuf_AppendStr(&buf, ")");
 
 
-	//Unique Non Stackable Item ID
-	updateLastUid(msg->item.unique_id);
-	dbUpdateUid(sql_handle);
-
 	// prepare and execute query
 	// prepare and execute query
 	stmt = SqlStmt_Malloc(sql_handle);
 	stmt = SqlStmt_Malloc(sql_handle);
 	if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
 	if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))

+ 0 - 1
src/char/int_storage.h

@@ -14,7 +14,6 @@ int inter_guild_storage_delete(int guild_id);
 
 
 int inter_storage_parse_frommap(int fd);
 int inter_storage_parse_frommap(int fd);
 
 
-//Exported for use in the TXT-SQL converter.
 int storage_fromsql(int account_id, struct storage_data* p);
 int storage_fromsql(int account_id, struct storage_data* p);
 int storage_tosql(int account_id,struct storage_data *p);
 int storage_tosql(int account_id,struct storage_data *p);
 int guild_storage_tosql(int guild_id, struct guild_storage *p);
 int guild_storage_tosql(int guild_id, struct guild_storage *p);

+ 0 - 19
src/char/inter.c

@@ -1168,23 +1168,4 @@ int inter_parse_frommap(int fd)
 	return 1;
 	return 1;
 }
 }
 
 
-uint64 inter_chk_lastuid(int8 flag, uint64 value){
-	static uint64 last_updt_uid = 0;
-	static int8 update = 0;
-	if(flag)
-	{
-		if(last_updt_uid < value){
-			last_updt_uid = value;
-			update = 1;
-		}
-
-		return 0;
-	}else if(update)
-	{
-		update = 0;
-		return last_updt_uid;
-	}
-	return 0;
-}
-
 
 

+ 0 - 14
src/char/inter.h

@@ -25,18 +25,4 @@ extern Sql* lsql_handle;
 
 
 int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type);
 int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type);
 
 
-uint64 inter_chk_lastuid(int8 flag, uint64 value);
-#ifdef NSI_UNIQUE_ID
-	#define updateLastUid(val_) inter_chk_lastuid(1, val_)
-	#define dbUpdateUid(handler_)\
-	{ \
-		uint64 unique_id_ = inter_chk_lastuid(0, 0); \
-		if (unique_id_ && SQL_ERROR == Sql_Query(handler_, "UPDATE `interreg` SET `value`='%"PRIu64"' WHERE `varname`='unique_id'", unique_id_)) \
-				Sql_ShowDebug(handler_);\
-	}
-#else
-	#define dbUpdateUid(handler_)
-	#define updateLastUid(val_)
-#endif
-
 #endif /* _INTER_SQL_H_ */
 #endif /* _INTER_SQL_H_ */

+ 2 - 0
src/common/mmo.h

@@ -420,6 +420,8 @@ struct mmo_charstatus {
 	unsigned char font;
 	unsigned char font;
 
 
 	bool cashshop_sent; // Whether the player has received the CashShop list
 	bool cashshop_sent; // Whether the player has received the CashShop list
+
+	uint32 uniqueitem_counter;
 };
 };
 
 
 typedef enum mail_status {
 typedef enum mail_status {

+ 0 - 4
src/config/core.h

@@ -48,10 +48,6 @@
 /// - but is not the official behaviour.
 /// - but is not the official behaviour.
 //#define CIRCULAR_AREA
 //#define CIRCULAR_AREA
 
 
-/// Uncomment to enable Non Stackable items unique ID
-/// By enabling it, the system will create an unique id for each new non stackable item created
-//#define NSI_UNIQUE_ID
-
 /// Comment to disable Guild/Party Bound item system
 /// Comment to disable Guild/Party Bound item system
 /// By default, we recover/remove Guild/Party Bound items automatically
 /// By default, we recover/remove Guild/Party Bound items automatically
 #define BOUND_ITEMS
 #define BOUND_ITEMS

+ 6 - 46
src/map/itemdb.c

@@ -1430,52 +1430,13 @@ static int itemdb_read_sqldb(void) {
 	return 0;
 	return 0;
 }
 }
 
 
-/** Unique item ID function
-* Only one operation by once
-* @param flag
-* 0 return new id
-* 1 set new value, checked with current value
-* 2 set new value bypassing anything
-* 3/other
-* @param value
-* @return last value
-*------------------------------------------*/
-uint64 itemdb_unique_id(int8 flag, int64 value) {
-	static uint64 item_uid = 0;
-
-	if(flag)
-	{
-		if(flag == 1)
-		{	if(item_uid < value)
-				return (item_uid = value);
-		}else if(flag == 2)
-			return (item_uid = value);
-
-		return item_uid;
-	}
-
-	return ++item_uid;
-}
-
 /**
 /**
-* Load Unique ID for Item
-*/
-static void itemdb_uid_load(void){
-
-	char * uid;
-	if (SQL_ERROR == Sql_Query(mmysql_handle, "SELECT `value` FROM `interreg` WHERE `varname`='unique_id'"))
-		Sql_ShowDebug(mmysql_handle);
-
-	if( SQL_SUCCESS != Sql_NextRow(mmysql_handle) )
-	{
-		ShowError("itemdb_uid_load: Unable to fetch unique_id data\n");
-		Sql_FreeResult(mmysql_handle);
-		return;
-	}
-
-	Sql_GetData(mmysql_handle, 0, &uid, NULL);
-	itemdb_unique_id(1, (uint64)strtoull(uid, NULL, 10));
-	Sql_FreeResult(mmysql_handle);
+ * Unique item ID function
+ * @param sd : Player
+ * @return unique_id
+ */
+uint64 itemdb_unique_id(struct map_session_data *sd) {
+	return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
 }
 }
 
 
 /** Check if the item is restricted by item_noequip.txt
 /** Check if the item is restricted by item_noequip.txt
@@ -1542,7 +1503,6 @@ static void itemdb_read(void) {
 		aFree(dbsubpath1);
 		aFree(dbsubpath1);
 		aFree(dbsubpath2);
 		aFree(dbsubpath2);
 	}
 	}
-	itemdb_uid_load();
 }
 }
 
 
 /*==========================================
 /*==========================================

+ 1 - 1
src/map/itemdb.h

@@ -479,7 +479,7 @@ bool itemdb_isequip2(struct item_data *id);
 char itemdb_isidentified(unsigned short nameid);
 char itemdb_isidentified(unsigned short nameid);
 bool itemdb_isstackable2(struct item_data *id);
 bool itemdb_isstackable2(struct item_data *id);
 #define itemdb_isstackable(nameid) itemdb_isstackable2(itemdb_search(nameid))
 #define itemdb_isstackable(nameid) itemdb_isstackable2(itemdb_search(nameid))
-uint64 itemdb_unique_id(int8 flag, int64 value); // Unique Item ID
+uint64 itemdb_unique_id(struct map_session_data *sd); // Unique Item ID
 bool itemdb_isNoEquip(struct item_data *id, uint16 m);
 bool itemdb_isNoEquip(struct item_data *id, uint16 m);
 
 
 struct item_combo *itemdb_combo_exists(unsigned short combo_id);
 struct item_combo *itemdb_combo_exists(unsigned short combo_id);

+ 19 - 6
src/map/pc.c

@@ -4308,10 +4308,8 @@ char pc_additem(struct map_session_data *sd,struct item *item,int amount,e_log_p
 		sd->last_addeditem_index = i;
 		sd->last_addeditem_index = i;
 		clif_additem(sd,i,amount,0);
 		clif_additem(sd,i,amount,0);
 	}
 	}
-#ifdef NSI_UNIQUE_ID
 	if( !itemdb_isstackable2(id) && !item->unique_id )
 	if( !itemdb_isstackable2(id) && !item->unique_id )
-		sd->status.inventory[i].unique_id = itemdb_unique_id(0,0);
-#endif
+		sd->status.inventory[i].unique_id = itemdb_unique_id(sd);
 	log_pick_pc(sd, log_type, amount, &sd->status.inventory[i]);
 	log_pick_pc(sd, log_type, amount, &sd->status.inventory[i]);
 
 
 	sd->weight += w;
 	sd->weight += w;
@@ -9518,12 +9516,17 @@ void pc_check_available_item(struct map_session_data *sd) {
 		for( i = 0; i < MAX_INVENTORY; i++ ) {
 		for( i = 0; i < MAX_INVENTORY; i++ ) {
 			it = sd->status.inventory[i].nameid;
 			it = sd->status.inventory[i].nameid;
 
 
-			if( it && !itemdb_available(it) ) {
+			if (!it)
+				continue;
+			if (!itemdb_available(it)) {
 				sprintf(output, msg_txt(sd, 709), it); // Item %hu has been removed from your inventory.
 				sprintf(output, msg_txt(sd, 709), it); // Item %hu has been removed from your inventory.
 				clif_displaymessage(sd->fd, output);
 				clif_displaymessage(sd->fd, output);
 				ShowWarning("Removed invalid/disabled item id %hu from inventory (amount=%d, char_id=%d).\n", it, sd->status.inventory[i].amount, sd->status.char_id);
 				ShowWarning("Removed invalid/disabled item id %hu from inventory (amount=%d, char_id=%d).\n", it, sd->status.inventory[i].amount, sd->status.char_id);
 				pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_OTHER);
 				pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_OTHER);
+				continue;
 			}
 			}
+			if (!sd->status.inventory[i].unique_id && !itemdb_isstackable(it))
+				sd->status.inventory[i].unique_id = itemdb_unique_id(sd);
 		}
 		}
 	}
 	}
 
 
@@ -9531,12 +9534,17 @@ void pc_check_available_item(struct map_session_data *sd) {
 		for( i = 0; i < MAX_CART; i++ ) {
 		for( i = 0; i < MAX_CART; i++ ) {
 			it = sd->status.cart[i].nameid;
 			it = sd->status.cart[i].nameid;
 
 
-			if( it && !itemdb_available(it) ) {
+			if (!it)
+				continue;
+			if (!itemdb_available(it)) {
 				sprintf(output, msg_txt(sd, 710), it); // Item %hu has been removed from your cart.
 				sprintf(output, msg_txt(sd, 710), it); // Item %hu has been removed from your cart.
 				clif_displaymessage(sd->fd, output);
 				clif_displaymessage(sd->fd, output);
 				ShowWarning("Removed invalid/disabled item id %hu from cart (amount=%d, char_id=%d).\n", it, sd->status.cart[i].amount, sd->status.char_id);
 				ShowWarning("Removed invalid/disabled item id %hu from cart (amount=%d, char_id=%d).\n", it, sd->status.cart[i].amount, sd->status.char_id);
 				pc_cart_delitem(sd, i, sd->status.cart[i].amount, 0, LOG_TYPE_OTHER);
 				pc_cart_delitem(sd, i, sd->status.cart[i].amount, 0, LOG_TYPE_OTHER);
+				continue;
 			}
 			}
+			if (!sd->status.cart[i].unique_id && !itemdb_isstackable(it))
+				sd->status.cart[i].unique_id = itemdb_unique_id(sd);
 		}
 		}
 	}
 	}
 
 
@@ -9544,12 +9552,17 @@ void pc_check_available_item(struct map_session_data *sd) {
 		for( i = 0; i < sd->storage_size; i++ ) {
 		for( i = 0; i < sd->storage_size; i++ ) {
 			it = sd->status.storage.items[i].nameid;
 			it = sd->status.storage.items[i].nameid;
 
 
-			if( it && !itemdb_available(it) ) {
+			if (!it)
+				continue;
+			if (!itemdb_available(it)) {
 				sprintf(output, msg_txt(sd, 711), it); // Item %hu has been removed from your storage.
 				sprintf(output, msg_txt(sd, 711), it); // Item %hu has been removed from your storage.
 				clif_displaymessage(sd->fd, output);
 				clif_displaymessage(sd->fd, output);
 				ShowWarning("Removed invalid/disabled item id %hu from storage (amount=%d, char_id=%d).\n", it, sd->status.storage.items[i].amount, sd->status.char_id);
 				ShowWarning("Removed invalid/disabled item id %hu from storage (amount=%d, char_id=%d).\n", it, sd->status.storage.items[i].amount, sd->status.char_id);
 				storage_delitem(sd, i, sd->status.storage.items[i].amount);
 				storage_delitem(sd, i, sd->status.storage.items[i].amount);
+				continue;
 			}
 			}
+			if (!sd->status.storage.items[i].unique_id && !itemdb_isstackable(it))
+				sd->status.storage.items[i].unique_id = itemdb_unique_id(sd);
  		}
  		}
 	}
 	}
 }
 }

+ 41 - 0
src/map/script.c

@@ -7793,6 +7793,46 @@ BUILDIN_FUNC(getequipid)
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
 }
 }
 
 
+/*==========================================
+ * GetEquipUniqueID(Pos);     Pos: 1-14
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipuniqueid)
+{
+	int i, num;
+	TBL_PC* sd;
+	struct item* item;
+
+	sd = script_rid2sd(st);
+	if (sd == NULL)
+		return 0;
+
+	num = script_getnum(st,2) - 1;
+	if (num < 0 || num >= ARRAYLENGTH(equip)) {
+		script_pushconststr(st, "");
+		return 0;
+	}
+
+	// get inventory position of item
+	i = pc_checkequip(sd,equip[num]);
+	if (i < 0) {
+		script_pushconststr(st, "");
+		return 0;
+	}
+
+	item = &sd->status.inventory[i];
+	if (item != 0) {
+		char buf[256];
+
+		memset(buf, 0, sizeof(buf));
+		snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long)item->unique_id);
+
+		script_pushstr(st, buf);
+	} else
+		script_pushconststr(st, "");
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
 /*==========================================
 /*==========================================
  * Get the equipement name at pos
  * Get the equipement name at pos
  * return item jname or ""
  * return item jname or ""
@@ -19108,6 +19148,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(strcharinfo,"i"),
 	BUILDIN_DEF(strcharinfo,"i"),
 	BUILDIN_DEF(strnpcinfo,"i"),
 	BUILDIN_DEF(strnpcinfo,"i"),
 	BUILDIN_DEF(getequipid,"i"),
 	BUILDIN_DEF(getequipid,"i"),
+	BUILDIN_DEF(getequipuniqueid,"i"),
 	BUILDIN_DEF(getequipname,"i"),
 	BUILDIN_DEF(getequipname,"i"),
 	BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
 	BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
 	BUILDIN_DEF(repair,"i"), // [Valaris]
 	BUILDIN_DEF(repair,"i"), // [Valaris]