Browse Source

Corrected `guid` implementation of item package in 51074a0
* GUID
- `guid` field in official PackageItem.lua supposed to make item will be separated even item is stackable item.
- Currently `guid` usage is using item Unique ID that generated when item with GUID flag is appear from package.
- Some item containers in official PackageItem.lua are also separated, not stacked when player obtain it. Example when using atcommand "@item Xmas_Bless". For current implementation, the items are flagged in item_flag.txt (db folder) with flag 4.
- `isNamed` field in item group is replaced by `GUID` and move the `isNamed` as new additional field. See doc/item_group.txt or db/re/item_package.txt.
* NPC:
- Enabled npc/re/other/item_merge.txt and it's now usable.
* Script Command:
- Added `mergeitem` to merge separated items in player's inventory. See doc/script_commands.txt.
* Misc:
- Changed how to broadcast the item that obtained from package with flag `isAnnounced` to intif_broadcast, so it will be announced to all connected map-servers.
- Added some items that flagged as 'item group container' in db/re/item_flag.txt

Signed-off-by: Cydh Ramdh <house.bad@gmail.com>

Cydh Ramdh 10 years ago
parent
commit
5ce80555a7

+ 1 - 0
db/import-tmpl/item_flag.txt

@@ -4,4 +4,5 @@
 // <Flag>:
 // 1 - As Dead Branch item (will be logged at `branchlog` table and cannot be used at 'nobranch' mapflag)
 // 2 - As item group container, check player's inventory and weight before consumed
+// 4 - GUID item, cannot be stacked even same or stackable item
 // NOTE: For removing flag by import file, use "-" to remove the flag. Example, 604,-1 will removes flag 1 from Branch_Of_Dead_Tree

+ 1 - 0
db/pre-re/item_flag.txt

@@ -4,6 +4,7 @@
 // <Flag>:
 // 1 - As Dead Branch item (will be logged at `branchlog` table and cannot be used at 'nobranch' mapflag)
 // 2 - As item group container, check player's inventory and weight before consumed
+// 4 - GUID item, cannot be stacked even same or stackable item
 // NOTE: For removing flag by import file, use "-" to remove the flag. Example, 604,-1 will removes flag 1 from Branch_Of_Dead_Tree
 
 // Logged as Dead Branch item

+ 266 - 0
db/re/item_flag.txt

@@ -4,6 +4,7 @@
 // <Flag>:
 // 1 - As Dead Branch item (will be logged at `branchlog` table and cannot be used at 'nobranch' mapflag)
 // 2 - As item group container, check player's inventory and weight before consumed
+// 4 - GUID item, cannot be stacked even same or stackable item
 // NOTE: For removing flag by import file, use "-" to remove the flag. Example, 604,-1 will removes flag 1 from Branch_Of_Dead_Tree
 
 // Logged as Dead Branch item
@@ -213,11 +214,19 @@
 13909,2 //MVP_Hunt_Box
 13910,2 //Brewing_Box
 13911,2 //Christmas_Pet_Scroll
+13925,2 //Lucky_Scroll08
+13945,2 //Br_SwordPackage
+13946,2 //Br_MagePackage
+13947,2 //Br_AcolPackage
+13948,2 //Br_ArcherPackage
+13949,2 //Br_MerPackage
+13950,2 //Br_ThiefPackage
 13953,2 //All_In_One_Ring_Box
 13989,2 //Acidbomb_10_Box
 14001,2 //Basic_Siege_Supply_Box
 14002,2 //Adv_Siege_Supply_Box
 14003,2 //Elite_Siege_Supply_Box
+14229,2 //Sakura_Scroll
 14242,2 //Beholder_Ring_Box
 14243,2 //Hallow_Ring_Box
 14244,2 //Clamorous_Ring_Box
@@ -242,6 +251,7 @@
 14469,2 //Ox_Tail_Scroll
 14596,2 //Pierre_Treasurebox
 14613,2 //RWC_Scroll_2012
+14624,2 //Blue_Scroll
 14626,2 //Indigo_Scroll
 14643,2 //Violet_Scroll
 14664,2 //Bi_Hwang_Scroll
@@ -366,3 +376,259 @@
 22558,2 //Lucky_Bag
 22669,2 //HALLOWEEN_G_BOX
 22685,2 //Solo_Christmas_Gift
+
+// GUID
+12915,6 //Aspersio_5_Scroll_Box
+12923,6 //Pet_Egg_Scroll_Box1
+12924,6 //Pet_Egg_Scroll_Box2
+12925,6 //Pet_Egg_Scroll1
+12926,6 //Pet_Egg_Scroll2
+12929,6 //Pet_Egg_Scroll_Box3
+12930,6 //Pet_Egg_Scroll_Box4
+12931,6 //Pet_Egg_Scroll_Box5
+12932,6 //Pet_Egg_Scroll3
+12933,6 //Pet_Egg_Scroll4
+12934,6 //Pet_Egg_Scroll5
+12935,6 //Infiltrator_Box
+12936,6 //Muramasa_Box
+12937,6 //Excalibur_Box
+12938,6 //Combat_Knife_Box
+12939,6 //Counter_Dagger_Box
+12940,6 //Kaiser_Knuckle_Box
+12941,6 //Pole_Axe_Box
+12942,6 //Mighty_Staff_Box
+12943,6 //Right_Epsilon_Box
+12944,6 //Balistar_Box
+12945,6 //Diary_Of_Great_Sage_Box
+12946,6 //Asura_Box
+12947,6 //Apple_Of_Archer_Box
+12948,6 //Bunny_Band_Box
+12949,6 //Sahkkat_Box
+12950,6 //Lord_Circlet_Box
+12951,6 //Elven_Ears_Box
+12952,6 //Steel_Flower_Box
+12953,6 //Critical_Ring_Box
+12954,6 //Earring_Box
+12955,6 //Ring_Box
+12956,6 //Necklace_Box
+12957,6 //Glove_Box
+12958,6 //Brooch_Box
+12959,6 //Rosary_Box
+12960,6 //Safety_Ring_Box
+12961,6 //Vesper_Core01_Box
+12962,6 //Vesper_Core02_Box
+12963,6 //Vesper_Core03_Box
+12964,6 //Vesper_Core04_Box
+12983,6 //Pet_Egg_Scroll_Box6
+12984,6 //Pet_Egg_Scroll_Box7
+12985,6 //Pet_Egg_Scroll_Box8
+12986,6 //Pet_Egg_Scroll_Box9
+12987,6 //Pet_Egg_Scroll_Box10
+12988,6 //Pet_Egg_Scroll_Box11
+12989,6 //Pet_Egg_Scroll6
+12990,6 //Pet_Egg_Scroll7
+12991,6 //Pet_Egg_Scroll8
+12992,6 //Pet_Egg_Scroll9
+12993,6 //Pet_Egg_Scroll10
+12994,6 //Pet_Egg_Scroll11
+13543,6 //CP_Helm_Scroll_Box
+13544,6 //CP_Shield_Scroll_Box
+13545,6 //CP_Armor_Scroll_Box
+13546,6 //CP_Weapon_Scroll_Box
+13547,6 //Repair_Scroll_Box
+13617,6 //Super_Pet_Egg1
+13618,6 //Super_Pet_Egg2
+13619,6 //Super_Pet_Egg3
+13620,6 //Super_Pet_Egg4
+13630,6 //Super_Card_Pet_Egg1
+13631,6 //Super_Card_Pet_Egg2
+13632,6 //Super_Card_Pet_Egg3
+13633,6 //Super_Card_Pet_Egg4
+13634,6 //Vigorgra_Package1
+13635,6 //Vigorgra_Package2
+13636,6 //Vigorgra_Package3
+13637,6 //Vigorgra_Package4
+13638,6 //Vigorgra_Package5
+13639,6 //Vigorgra_Package6
+13640,6 //Vigorgra_Package7
+13641,6 //Vigorgra_Package8
+13642,6 //Vigorgra_Package9
+13643,6 //Vigorgra_Package10
+13644,6 //Vigorgra_Package11
+13645,6 //Vigorgra_Package12
+13701,6 //Pet_Egg_Scroll12
+13702,6 //Pet_Egg_Scroll13
+13703,6 //Pet_Egg_Scroll14
+13704,6 //Super_Pet_Egg5
+13705,6 //Super_Pet_Egg6
+13706,6 //Super_Pet_Egg7
+13707,6 //Super_Pet_Egg8
+13708,6 //Pet_Egg_Scroll_E
+13725,6 //Ramen_Hat_Box
+13773,6 //Fire_Brand_Box
+13845,6 //Mysterious_Travel_Sack1
+13846,6 //Mysterious_Travel_Sack2
+13847,6 //Mysterious_Travel_Sack3
+13848,6 //Mysterious_Travel_Sack4
+13871,6 //Magician_Card_Box
+13872,6 //Acolyte_Card_Box
+13873,6 //Archer_Card_Box
+13874,6 //Swordman_Card_Box
+13875,6 //Thief_Card_Box
+13876,6 //Merchant_Card_Box
+13905,6 //Hard_Core_Set_Box
+13906,6 //Kitty_Set_Box
+13907,6 //Soft_Core_Set_Box
+13908,6 //Deviruchi_Set_Box
+13909,6 //MVP_Hunt_Box
+13910,6 //Brewing_Box
+13911,6 //Christmas_Pet_Scroll
+13925,6 //Lucky_Scroll08
+13945,6 //Br_SwordPackage
+13946,6 //Br_MagePackage
+13947,6 //Br_AcolPackage
+13948,6 //Br_ArcherPackage
+13949,6 //Br_MerPackage
+13950,6 //Br_ThiefPackage
+13953,6 //All_In_One_Ring_Box
+13989,6 //Acidbomb_10_Box
+14001,6 //Basic_Siege_Supply_Box
+14002,6 //Adv_Siege_Supply_Box
+14003,6 //Elite_Siege_Supply_Box
+14229,6 //Sakura_Scroll
+14242,6 //Beholder_Ring_Box
+14243,6 //Hallow_Ring_Box
+14244,6 //Clamorous_Ring_Box
+14245,6 //Chemical_Ring_Box
+14246,6 //Insecticide_Ring_Box
+14247,6 //Fisher_Ring_Box
+14248,6 //Decussate_Ring_Box
+14249,6 //Bloody_Ring_Box
+14250,6 //Satanic_Ring_Box
+14251,6 //Dragoon_Ring_Box
+14296,6 //Angel_Scroll
+14297,6 //Devil_Scroll
+14298,6 //Surprise_Scroll
+14306,6 //RWC_Special_Scroll
+14307,6 //RWC_Limited_Scroll
+14316,6 //July7_Scroll
+14317,6 //Bacsojin_Scroll
+14345,6 //Animal_Scroll
+14363,6 //Heart_Scroll
+14408,6 //New_Year_Scroll
+14466,6 //Valentine_Pledge_Box
+14469,6 //Ox_Tail_Scroll
+16245,6 //Tw_April_Scroll
+16304,6 //Evil_Incarnation
+16371,6 //Tw_Aug_Scroll
+16372,6 //F_Clover_Box_Mouth
+16374,6 //Mouth_Bubble_Gum_Box
+16385,6 //F_Clover_Box_Mouth2
+16386,6 //F_Clover_Box_Mouth4
+16389,6 //BGum_Box_In_Mouth2
+16390,6 //BGum_Box_In_Mouth4
+16409,6 //Tw_Sep_Scroll
+16446,6 //Tw_October_Scroll
+16456,6 //My_Scroll1
+16457,6 //Tw_Nov_Scroll
+16466,6 //My_Scroll2
+16542,6 //Xmas_Bless
+16555,6 //Pr_Reset_Stone_Box
+16556,6 //FPr_Reset_Stone_Box
+16562,6 //Majestic_Devil_Scroll
+16576,6 //Illusion_Nothing
+16638,6 //Life_Ribbon_Box
+16639,6 //Life_Ribbon_Box2
+16640,6 //Life_Ribbon_Box3
+16652,6 //Flame_Light
+16666,6 //Magic_Candy_Box10
+16673,6 //Libra_Scroll
+16675,6 //Splash_Scroll
+16681,6 //BR_Independence_Scroll
+16682,6 //Boarding_Halter_Box
+16687,6 //RWC2010_SuitcaseA
+16688,6 //RWC2010_SuitcaseB
+16741,6 //Hairtail_Box1
+16742,6 //Hairtail_Box2
+16743,6 //Spearfish_Box1
+16744,6 //Spearfish_Box2
+16745,6 //Saurel_Box1
+16746,6 //Saurel_Box2
+16747,6 //Tuna_Box1
+16748,6 //Tuna_Box2
+16749,6 //Malang_Crab_Box1
+16750,6 //Malang_Crab_Box2
+16751,6 //Brindle_Eel_Box1
+16752,6 //Brindle_Eel_Box2
+16757,6 //Hallo_Scroll
+16760,6 //Umbala_Spirit_Box2
+16761,6 //F_Umbala_Spirit_Box2
+16763,6 //Ptotection_Seagod_Box2
+16764,6 //Ptotection_Seagod_Box3
+16765,6 //Octo_Hstick_Box
+16766,6 //Octo_Hstick_Box2
+16767,6 //Octo_Hstick_Box3
+16770,6 //Silvervine_Fruit_Box10
+16771,6 //Silvervine_Fruit_Box40
+16774,6 //Asgard_Scroll
+16775,6 //Sagittarius_Scroll
+16826,6 //Sagittarius_Scr_Box
+16972,6 //Weather_Report_Box
+16974,6 //Comin_Actor_Box
+16976,6 //Hen_Set_Box
+16979,6 //Silvervine_Fruit_Box4
+16990,6 //Sagittar_Diadem_Scroll
+16991,6 //Sagittar_Di_Scroll_Box
+16996,6 //Capri_Crown_Scroll
+16997,6 //Capri_Crown_Scroll_Box
+17011,6 //Capricon_Di_Scroll
+17012,6 //Capricon_Di_Scroll_Box
+17013,6 //Malang_Woe_Encard_Box
+17016,6 //Aquarius_Diadem_Scroll
+17017,6 //Aquarius_Di_Scroll_Box
+17020,6 //Tw_Nov_Scroll2
+17021,6 //Summer_Scroll3
+17022,6 //Super_Pet_Egg1_2
+17023,6 //Super_Pet_Egg4_2
+17024,6 //Lovely_Aquarius_Scroll
+17025,6 //Lovely_Aquarius_Box
+17026,6 //Boitata_Scroll
+17028,6 //Pisces_Diadem_Scroll
+17029,6 //Pisces_Diadem_Box
+17035,6 //Energetic_Pisces_Scroll
+17036,6 //Energetic_Pisces_Box
+17050,6 //Aries_Scroll
+17051,6 //Aries_Scroll_Box
+17062,6 //Taurus_Diadem_Scroll
+17063,6 //Taurus_Di_Scroll_Box
+17077,6 //Taurus_Crown_Scroll
+17078,6 //Taurus_Crown_Scroll_Box
+17082,6 //Gemi_Diadem_Scroll
+17083,6 //Gemi_Diadem_Scroll_Box
+17107,6 //Gemi_Crown_Scroll
+17108,6 //Gemi_Crown_Scroll_Box
+17138,6 //Ms_Cancer_Scroll
+17139,6 //RWC_Super_Scroll
+17140,6 //Leo_Scroll
+17141,6 //Ms_Virgo_Scroll
+17143,6 //Ms_Scorpio_Scroll
+17156,6 //TCG_Card_Scroll
+17165,6 //Challenge_Kit
+17209,6 //Tw_Rainbow_Scroll
+17210,6 //Tw_Red_Scroll
+17211,6 //Tw_Orange_Scroll
+17212,6 //Tw_Yellow_Scroll
+17233,6 //Scroll_Of_Death
+17234,6 //Scroll_Of_Life
+17235,6 //Scroll_Of_Magic
+17236,6 //Scroll_Of_Thews
+17237,6 //Scroll_Of_Darkness
+17238,6 //Scroll_Of_Holiness
+17239,6 //Horned_Scroll
+17240,6 //Mercury_Scroll
+17251,6 //C_Wing_Of_Fly_3Day_Box
+17252,6 //RWC_2012_Set_Box
+17256,6 //Good_Student_Gift_Box
+17257,6 //Bad_Student_Gift_Box
+17262,6 //Ex_Def_Potion_Box
+22558,6 //Lucky_Bag

+ 1 - 1
db/re/item_package.txt

@@ -1,7 +1,7 @@
 // Item Package Database
 //
 // Structure of Database:
-// GroupID,ItemID,Rate{,Amount,Random,isAnnounced,Duration,isNamed,isBound}
+// GroupID,ItemID,Rate{,Amount,Random,isAnnounced,Duration,GUID,isBound,isNamed}
 
 IG_Special_Box,Wrapped_Mask,3,1,1
 IG_Special_Box,Poison_Bottle,10,2,1

+ 13 - 2
doc/item_group.txt

@@ -29,10 +29,12 @@ accessed in each.
 +===============+====================+================+
 | Duration      |       no           |      YES       |
 +===============+====================+================+
-| isNamed       |       no           |      YES       |
+| GUID          |       no           |      YES       |
 +===============+====================+================+
 | isBound       |       no           |      YES       |
 +===============+====================+================+
+| isNamed       |       no           |      YES       |
++===============+====================+================+
 
 ---------------------------------------
 
@@ -117,7 +119,12 @@ Duration: Makes the item a rental item, which will be expire in the given amount
 
 ---------------------------------------
 
-isNamed: Inscribes the item with the obtainer's name.
+GUID: Makes the given item(s) with Unique ID. Item will be stacked ONLY each group
+      when it obtained. Cannot be stacked with same item, even it's stackable item.
+	  Example, there is Box (just call it Apple_Box) that contains 3x Apples with
+	  GUID = 1. When Apples appear it will stack for each 3 even another 3x Apples
+	  are appeared by same box. So it will be filled in inventory as:
+	          3x Apples | 3x Apples | so on... | nx Apples (normal)
 
 ---------------------------------------
 
@@ -126,6 +133,10 @@ isBound: Binds the obtained item.
 
 ---------------------------------------
 
+isNamed: Inscribes the item with the obtainer's name.
+
+---------------------------------------
+
 Supports to import other file, usage:
 import: db/path/filename.txt
 

+ 11 - 0
doc/script_commands.txt

@@ -2841,6 +2841,17 @@ Returns value from equipped item slot in the indicated slot (0, 1, 2, or 3).
 This function returns CARD ID, 255,254,-255 (for card 0, if the item is produced).
 It's useful for when you want to check whether an item contains cards or if it's signed.
 
+---------------------------------------
+
+*mergeitem({<item_id>});
+*mergeitem({"<item name>"});
+
+Merge item!!!!!!!!!
+
+If no item_id ot "item name", it will merges all possible items that exist in player's inventory.
+
+Example, just see the NPC npc/re/other/merge_item.txt
+
 ---------------------------------------
 //
 2,1.- End of item-related commands.

+ 3 - 2
npc/re/other/item_merge.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= Euphy
 //===== Current Version: =====================================
-//= 1.0
+//= 1.1
 //===== Compatible With: =====================================
 //= rAthena Project
 //===== Description: =========================================
@@ -12,6 +12,7 @@
 //= inventory.
 //===== Additional Comments: =================================
 //= 1.0 First version, currently useless/disabled.
+//= 1.1 Implemented the 'mergeitem' script command. [Cydh]
 //============================================================
 
 prontera,146,95,3	script	Mergician#pron	64,{
@@ -55,7 +56,7 @@ prontera,146,95,3	script	Mergician#pron	64,{
 		next;
 		switch(select("Merrrrge!:Don't follow what he says.")) {
 		case 1:
-//			MergeItem
+			mergeitem;
 			mes "[Mergician]";
 			mes "Merge just heard your wish and let it be realised!";
 			mes "Open your inventory to check the miracle!";

+ 1 - 1
npc/re/scripts_athena.conf

@@ -90,7 +90,7 @@ npc: npc/re/merchants/shops.txt
 
 // --------------------------- Others ---------------------------
 npc: npc/re/other/bulletin_boards.txt
-//npc: npc/re/other/item_merge.txt
+npc: npc/re/other/item_merge.txt
 npc: npc/re/other/mail.txt
 npc: npc/re/other/mercenary_rent.txt
 npc: npc/re/other/pvp.txt

+ 4 - 1
src/map/atcommand.c

@@ -1268,7 +1268,8 @@ ACMD_FUNC(item)
 				item_tmp.nameid = item_id;
 				item_tmp.identify = 1;
 				item_tmp.bound = bound;
-
+				if (item_data[j]->flag.guid)
+					item_tmp.unique_id = pc_generate_unique_id(sd);
 				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
 					clif_additem(sd, 0, 0, flag);
 			}
@@ -1359,6 +1360,8 @@ ACMD_FUNC(item2)
 			item_tmp.card[2] = c3;
 			item_tmp.card[3] = c4;
 			item_tmp.bound = bound;
+			if (item_data->flag.guid)
+				item_tmp.unique_id = pc_generate_unique_id(sd);
 			if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
 				clif_additem(sd, 0, 0, flag);
 		}

+ 6 - 2
src/map/clif.c

@@ -13206,8 +13206,12 @@ void clif_parse_GM_Item_Monster(int fd, struct map_session_data *sd)
 		StringBuf_Init(&command);
 		if( !itemdb_isstackable2(id) ) //Nonstackable
 			StringBuf_Printf(&command, "%citem2 %d 1 0 0 0 0 0 0 0", atcommand_symbol, id->nameid);
-		else
-			StringBuf_Printf(&command, "%citem %d 20", atcommand_symbol, id->nameid);
+		else {
+			if (id->flag.guid)
+				StringBuf_Printf(&command, "%citem %d 1", atcommand_symbol, id->nameid);
+			else
+				StringBuf_Printf(&command, "%citem %d 20", atcommand_symbol, id->nameid);
+		}
 		is_atcommand(fd, sd, StringBuf_Value(&command), 1);
 		StringBuf_Destroy(&command);
 		return;

+ 12 - 5
src/map/itemdb.c

@@ -13,6 +13,7 @@
 #include "cashshop.h"
 #include "script.h" // item script processing
 #include "pc.h"     // W_MUSICAL, W_WHIP
+#include "intif.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -198,12 +199,15 @@ static void itemdb_pc_get_itemgroup_sub(struct map_session_data *sd, struct s_it
 	// Do loop for non-stackable item
 	for (i = 0; i < data->amount; i++) {
 		char flag = 0;
+		tmp.unique_id = data->GUID ? pc_generate_unique_id(sd) : 0; // Generate UID
 		if ((flag = pc_additem(sd, &tmp, tmp.amount, LOG_TYPE_SCRIPT)))
 			clif_additem(sd, 0, 0, flag);
-		else if (!flag && data->isAnnounced) { ///TODO: Move this broadcast to proper behavior (it should on at different packet)
+		else if (!flag && data->isAnnounced) {
 			char output[CHAT_SIZE_MAX];
 			sprintf(output, msg_txt(NULL, 717), sd->status.name, itemdb_jname(data->nameid), itemdb_jname(sd->itemid));
-			clif_broadcast(&sd->bl, output, strlen(output), BC_DEFAULT, ALL_CLIENT);
+
+			//! TODO: Move this broadcast to proper packet
+			intif_broadcast(output, strlen(output) + 1, BC_DEFAULT);
 			//clif_broadcast_obtain_special_item();
 		}
 		if (itemdb_isstackable(data->nameid))
@@ -557,7 +561,7 @@ static bool itemdb_read_itemavail(char* str[], int columns, int current) {
 }
 
 /** Read item group data
-* Structure: GroupID,ItemID,Rate{,Amount,isMust,isAnnounced,Duration,isNamed,isBound}
+* Structure: GroupID,ItemID,Rate{,Amount,isMust,isAnnounced,Duration,GUID,isBound,isNamed}
 */
 static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
 {
@@ -574,7 +578,7 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
 		int group_id = -1;
 		unsigned int j, prob = 1;
 		uint8 rand_group = 1;
-		char *str[9], *p;
+		char *str[10], *p;
 		struct s_item_group_random *random = NULL;
 		struct s_item_group_db *group = NULL;
 		struct s_item_group_entry entry;
@@ -658,8 +662,9 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
 		if (str[3] != NULL) entry.amount = cap_value(atoi(str[3]),1,MAX_AMOUNT);
 		if (str[5] != NULL) entry.isAnnounced= atoi(str[5]);
 		if (str[6] != NULL) entry.duration = cap_value(atoi(str[6]),0,UINT16_MAX);
-		if (str[7] != NULL) entry.isNamed = atoi(str[7]);
+		if (str[7] != NULL) entry.GUID = atoi(str[7]);
 		if (str[8] != NULL) entry.bound = cap_value(atoi(str[8]),BOUND_NONE,BOUND_MAX-1);
+		if (str[9] != NULL) entry.isNamed = atoi(str[9]);
 
 		if (!(group = (struct s_item_group_db *) uidb_get(itemdb_group, group_id))) {
 			CREATE(group, struct s_item_group_db, 1);
@@ -894,6 +899,7 @@ static bool itemdb_read_nouse(char* fields[], int columns, int current) {
 * <item_id>,<flag>
 * &1 - As dead branch item
 * &2 - As item container
+* &4 - GUID item, cannot be stacked even same or stackable item
 */
 static bool itemdb_read_flag(char* fields[], int columns, int current) {
 	unsigned short nameid = atoi(fields[0]);
@@ -911,6 +917,7 @@ static bool itemdb_read_flag(char* fields[], int columns, int current) {
 
 	if (flag&1) id->flag.dead_branch = set ? 1 : 0;
 	if (flag&2) id->flag.group = set ? 1 : 0;
+	if (flag&4 && itemdb_isstackable2(id)) id->flag.guid = set ? 1 : 0;
 
 	return true;
 }

+ 2 - 0
src/map/itemdb.h

@@ -345,6 +345,7 @@ struct s_item_group_entry
 		duration, /// Duration if item as rental item (in minutes)
 		amount; /// Amount of item will be obtained
 	bool isAnnounced, /// Broadcast if player get this item
+		GUID, /// Gives Unique ID for items in each box opened
 		isNamed; /// Named the item (if possible)
 	char bound; /// Makes the item as bound item (according to bound type)
 };
@@ -414,6 +415,7 @@ struct item_data
 		unsigned buyingstore : 1;
 		unsigned dead_branch : 1; // As dead branch item. Logged at `branchlog` table and cannot be used at 'nobranch' mapflag [Cydh]
 		unsigned group : 1; // As item group container [Cydh]
+		unsigned guid : 1; // This item always be attached with GUID and make it as bound item! [Cydh]
 	} flag;
 	struct {// item stacking limitation
 		unsigned short amount;

+ 8 - 4
src/map/pc.c

@@ -4290,9 +4290,12 @@ char pc_additem(struct map_session_data *sd,struct item *item,int amount,e_log_p
 	if( itemdb_isstackable2(id) && item->expire_time == 0 ) {
 		for( i = 0; i < MAX_INVENTORY; i++ ) {
 			if( sd->status.inventory[i].nameid == item->nameid &&
-			    sd->status.inventory[i].bound == item->bound &&
-			    sd->status.inventory[i].expire_time == 0 &&
-			    memcmp(&sd->status.inventory[i].card, &item->card, sizeof(item->card)) == 0 ) {
+				sd->status.inventory[i].bound == item->bound &&
+				sd->status.inventory[i].expire_time == 0 &&
+				sd->status.inventory[i].unique_id == item->unique_id &&
+				memcmp(&sd->status.inventory[i].card, &item->card, sizeof(item->card)) == 0
+				)
+			{
 				if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( id->stack.inventory && amount > id->stack.amount - sd->status.inventory[i].amount ) )
 					return ADDITEM_OVERAMOUNT;
 				sd->status.inventory[i].amount += amount;
@@ -4321,7 +4324,7 @@ char pc_additem(struct map_session_data *sd,struct item *item,int amount,e_log_p
 		clif_additem(sd,i,amount,0);
 	}
 	if( !itemdb_isstackable2(id) && !item->unique_id )
-		sd->status.inventory[i].unique_id = pc_generate_unique_id(sd);
+		item->unique_id = pc_generate_unique_id(sd);
 	log_pick_pc(sd, log_type, amount, &sd->status.inventory[i]);
 
 	sd->weight += w;
@@ -11272,6 +11275,7 @@ bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short inde
  * @return A generated Unique item ID
  */
 uint64 pc_generate_unique_id(struct map_session_data *sd) {
+	nullpo_ret(sd);
 	return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
 }
 

+ 93 - 5
src/map/script.c

@@ -6497,20 +6497,21 @@ BUILDIN_FUNC(getitem)
 	struct script_data *data;
 	unsigned char flag = 0;
 	const char* command = script_getfuncname(st);
+	struct item_data *id = NULL;
 
-	data=script_getdata(st,2);
+	data = script_getdata(st,2);
 	get_val(st,data);
 	if( data_isstring(data) ) {// "<item name>"
 		const char *name = conv_str(st,data);
-		struct item_data *item_data = itemdb_searchname(name);
-		if( item_data == NULL ){
+		id = itemdb_searchname(name);
+		if( id == NULL ){
 			ShowError("buildin_getitem: Nonexistant item %s requested.\n", name);
 			return SCRIPT_CMD_FAILURE; //No item created.
 		}
-		nameid = item_data->nameid;
+		nameid = id->nameid;
 	} else if( data_isint(data) ) {// <item id>
 		nameid = conv_num(st,data);
-		if( !itemdb_exists(nameid) ){
+		if( !(id = itemdb_exists(nameid)) ){
 			ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
 			return SCRIPT_CMD_FAILURE; //No item created.
 		}
@@ -6558,6 +6559,7 @@ BUILDIN_FUNC(getitem)
 		// if not pet egg
 		if (!pet_create_egg(sd, nameid))
 		{
+			it.unique_id = (id->flag.guid) ? pc_generate_unique_id(sd) : 0;
 			if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
 			{
 				clif_additem(sd, 0, 0, flag);
@@ -6677,6 +6679,7 @@ BUILDIN_FUNC(getitem2)
 			if (!pet_create_egg(sd, nameid))
 			{
 				unsigned char flag = 0;
+				item_tmp.unique_id = (item_data->flag.guid) ? pc_generate_unique_id(sd) : 0;
 				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT)))
 				{
 					clif_additem(sd, 0, 0, flag);
@@ -19111,6 +19114,90 @@ BUILDIN_FUNC(countspiritball) {
 	return SCRIPT_CMD_SUCCESS;
 }
 
+/** Merges separated stackable items because of guid
+* mergeitem {<item_id>};
+* mergeitem {"<item name>"};
+* @param item Item ID/Name for merging specific item (Optional)
+* @author [Cydh]
+*/
+BUILDIN_FUNC(mergeitem) {
+	struct map_session_data *sd = script_rid2sd(st);
+	struct item *items;
+	uint16 i, count = 0;
+	int nameid = 0;
+
+	if (!sd)
+		return SCRIPT_CMD_SUCCESS;
+
+	if (script_hasdata(st, 2)) {
+		struct script_data *data = script_getdata(st, 2);
+		struct item_data *id;
+		get_val(st, data);
+
+		if (data_isstring(data)) {// "<item name>"
+			const char *name = conv_str(st,data);
+			if (!(id = itemdb_searchname(name))) {
+				ShowError("buildin_mergeitem: Nonexistant item %s requested.\n", name);
+				script_pushint(st, count);
+				return SCRIPT_CMD_FAILURE;
+			}
+			nameid = id->nameid;
+		}
+		else if (data_isint(data)) {// <item id>
+			nameid = conv_num(st,data);
+			if (!(id = itemdb_exists(nameid))) {
+				ShowError("buildin_mergeitem: Nonexistant item %d requested.\n", nameid);
+				script_pushint(st, count);
+				return SCRIPT_CMD_FAILURE;
+			}
+		}
+	}
+
+	for (i = 0; i < MAX_INVENTORY; i++) {
+		struct item *it = &sd->status.inventory[i];
+		if (!it || !it->unique_id || it->expire_time || !itemdb_isstackable(it->nameid))
+			continue;
+		if ((!nameid || (nameid == it->nameid))) {
+			uint8 k;
+			if (!count) {
+				CREATE(items, struct item, 1);
+				memcpy(&items[count++], it, sizeof(struct item));
+				pc_delitem(sd, i, it->amount, 0, 0, LOG_TYPE_NPC);
+				continue;
+			}
+			for (k = 0; k < count; k++) {
+				// Find Match
+				if (&items[k] && items[k].nameid == it->nameid && items[k].bound == it->bound) {
+					items[k].amount += it->amount;
+					pc_delitem(sd, i, it->amount, 0, 0, LOG_TYPE_NPC);
+					break;
+				}
+			}
+			if (k >= count) {
+				// New entry
+				RECREATE(items, struct item, count+1);
+				memcpy(&items[count++], it, sizeof(struct item));
+				pc_delitem(sd, i, it->amount, 0, 0, LOG_TYPE_NPC);
+			}
+		}
+	}
+
+	// Retrieve the items
+	for (i = 0; i < count; i++) {
+		uint8 flag = 0;
+		if (!&items[i])
+			continue;
+		items[i].id = 0;
+		//items[i].unique_id = pc_generate_unique_id(sd);
+		items[i].unique_id = 0;
+		if ((flag = pc_additem(sd, &items[i], items[i].amount, LOG_TYPE_NPC)))
+			clif_additem(sd, i, items[i].amount, flag);
+	}
+	aFree(items);
+	script_pushint(st, count);
+	return SCRIPT_CMD_SUCCESS;
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.c
@@ -19653,6 +19740,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(addspiritball,"ii?"),
 	BUILDIN_DEF(delspiritball,"i?"),
 	BUILDIN_DEF(countspiritball,"?"),
+	BUILDIN_DEF(mergeitem,"?"),
 
 #include "../custom/script_def.inc"
 

+ 3 - 1
src/map/storage.c

@@ -143,7 +143,9 @@ int compare_item(struct item *a, struct item *b)
 		a->refine == b->refine &&
 		a->attribute == b->attribute &&
 		a->expire_time == b->expire_time &&
-		a->bound == b->bound ) {
+		a->bound == b->bound &&
+		a->unique_id == b->unique_id )
+	{
 		int i;
 
 		for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);