瀏覽代碼

Merge pull request #143 from rathena/feature/separated_item_guid

Corrected `guid` implementation of item package in 51074a0. See http://rathena.org/board/topic/99743-item-guid/
Cydh Ramdh 10 年之前
父節點
當前提交
7295bdc925

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

@@ -4,4 +4,5 @@
 // <Flag>:
 // <Flag>:
 // 1 - As Dead Branch item (will be logged at `branchlog` table and cannot be used at 'nobranch' mapflag)
 // 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
 // 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
 // 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>:
 // <Flag>:
 // 1 - As Dead Branch item (will be logged at `branchlog` table and cannot be used at 'nobranch' mapflag)
 // 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
 // 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
 // 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
 // Logged as Dead Branch item

+ 266 - 0
db/re/item_flag.txt

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

+ 1 - 1
db/re/item_package.txt

@@ -1,7 +1,7 @@
 // Item Package Database
 // Item Package Database
 //
 //
 // Structure of 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,Wrapped_Mask,3,1,1
 IG_Special_Box,Poison_Bottle,10,2,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       |
 | Duration      |       no           |      YES       |
 +===============+====================+================+
 +===============+====================+================+
-| isNamed       |       no           |      YES       |
+| GUID          |       no           |      YES       |
 +===============+====================+================+
 +===============+====================+================+
 | isBound       |       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:
 Supports to import other file, usage:
 import: db/path/filename.txt
 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).
 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.
 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 all stackable items that separated by GUID flags
+(either by flag 4 item_flag.txt or GUID  in item_group).
+If no item ID/name given, all possible items in player's inventory will be merged.
+
+Example, just see the NPC 'npc/re/other/merge_item.txt'.
+
 ---------------------------------------
 ---------------------------------------
 //
 //
 2,1.- End of item-related commands.
 2,1.- End of item-related commands.

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

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

+ 8 - 0
src/config/core.h

@@ -64,6 +64,14 @@
 /// Uncomment to enable the job base HP/SP table (job_basehpsp_db.txt)
 /// Uncomment to enable the job base HP/SP table (job_basehpsp_db.txt)
 //#define HP_SP_TABLES
 //#define HP_SP_TABLES
 
 
+/// Enable separated item by `guid`. [Cydh]
+/// See db/[pre-]re/item_flag.txt and doc/item_group.txt for the `guid` explanation.
+/// NOTE:
+///    If this feature is disabled "in the middle" of your game, the separated is still
+///    separated in inventory, storage, or guild storage until player move the item
+///    to/from storage/inventory to restack them.
+#define ENABLE_ITEM_GUID
+
 /// Uncomment to enable VIP system.
 /// Uncomment to enable VIP system.
 //#define VIP_ENABLE
 //#define VIP_ENABLE
 
 

+ 0 - 1
src/map/atcommand.c

@@ -1271,7 +1271,6 @@ ACMD_FUNC(item)
 				item_tmp.nameid = item_id;
 				item_tmp.nameid = item_id;
 				item_tmp.identify = 1;
 				item_tmp.identify = 1;
 				item_tmp.bound = bound;
 				item_tmp.bound = bound;
-
 				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
 				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
 					clif_additem(sd, 0, 0, flag);
 					clif_additem(sd, 0, 0, flag);
 			}
 			}

+ 8 - 2
src/map/clif.c

@@ -13207,8 +13207,14 @@ void clif_parse_GM_Item_Monster(int fd, struct map_session_data *sd)
 		StringBuf_Init(&command);
 		StringBuf_Init(&command);
 		if( !itemdb_isstackable2(id) ) //Nonstackable
 		if( !itemdb_isstackable2(id) ) //Nonstackable
 			StringBuf_Printf(&command, "%citem2 %d 1 0 0 0 0 0 0 0", atcommand_symbol, id->nameid);
 			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 {
+#ifdef ENABLE_ITEM_GUID
+			if (id->flag.guid)
+				StringBuf_Printf(&command, "%citem %d 1", atcommand_symbol, id->nameid);
+			else
+#endif
+				StringBuf_Printf(&command, "%citem %d 20", atcommand_symbol, id->nameid);
+		}
 		is_atcommand(fd, sd, StringBuf_Value(&command), 1);
 		is_atcommand(fd, sd, StringBuf_Value(&command), 1);
 		StringBuf_Destroy(&command);
 		StringBuf_Destroy(&command);
 		return;
 		return;

+ 29 - 16
src/map/itemdb.c

@@ -13,6 +13,7 @@
 #include "cashshop.h"
 #include "cashshop.h"
 #include "script.h" // item script processing
 #include "script.h" // item script processing
 #include "pc.h"     // W_MUSICAL, W_WHIP
 #include "pc.h"     // W_MUSICAL, W_WHIP
+#include "intif.h"
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -198,12 +199,17 @@ static void itemdb_pc_get_itemgroup_sub(struct map_session_data *sd, struct s_it
 	// Do loop for non-stackable item
 	// Do loop for non-stackable item
 	for (i = 0; i < data->amount; i++) {
 	for (i = 0; i < data->amount; i++) {
 		char flag = 0;
 		char flag = 0;
+#ifdef ENABLE_ITEM_GUID
+		tmp.unique_id = data->GUID ? pc_generate_unique_id(sd) : 0; // Generate UID
+#endif
 		if ((flag = pc_additem(sd, &tmp, tmp.amount, LOG_TYPE_SCRIPT)))
 		if ((flag = pc_additem(sd, &tmp, tmp.amount, LOG_TYPE_SCRIPT)))
 			clif_additem(sd, 0, 0, flag);
 			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];
 			char output[CHAT_SIZE_MAX];
 			sprintf(output, msg_txt(NULL, 717), sd->status.name, itemdb_jname(data->nameid), itemdb_jname(sd->itemid));
 			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();
 			//clif_broadcast_obtain_special_item();
 		}
 		}
 		if (itemdb_isstackable(data->nameid))
 		if (itemdb_isstackable(data->nameid))
@@ -438,17 +444,17 @@ bool itemdb_isequip2(struct item_data *id)
 */
 */
 bool itemdb_isstackable2(struct item_data *id)
 bool itemdb_isstackable2(struct item_data *id)
 {
 {
-  nullpo_ret(id);
-  switch(id->type) {
-	  case IT_WEAPON:
-	  case IT_ARMOR:
-	  case IT_PETEGG:
-	  case IT_PETARMOR:
-	  case IT_SHADOWGEAR:
-		  return false;
-	  default:
-		  return true;
-  }
+	nullpo_ret(id);
+	switch(id->type) {
+		case IT_WEAPON:
+		case IT_ARMOR:
+		case IT_PETEGG:
+		case IT_PETARMOR:
+		case IT_SHADOWGEAR:
+			return false;
+		default:
+			return true;
+	}
 }
 }
 
 
 
 
@@ -557,7 +563,7 @@ static bool itemdb_read_itemavail(char* str[], int columns, int current) {
 }
 }
 
 
 /** Read item group data
 /** 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)
 static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
 {
 {
@@ -574,7 +580,7 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
 		int group_id = -1;
 		int group_id = -1;
 		unsigned int j, prob = 1;
 		unsigned int j, prob = 1;
 		uint8 rand_group = 1;
 		uint8 rand_group = 1;
-		char *str[9], *p;
+		char *str[10], *p;
 		struct s_item_group_random *random = NULL;
 		struct s_item_group_random *random = NULL;
 		struct s_item_group_db *group = NULL;
 		struct s_item_group_db *group = NULL;
 		struct s_item_group_entry entry;
 		struct s_item_group_entry entry;
@@ -658,8 +664,11 @@ 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[3] != NULL) entry.amount = cap_value(atoi(str[3]),1,MAX_AMOUNT);
 		if (str[5] != NULL) entry.isAnnounced= atoi(str[5]);
 		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[6] != NULL) entry.duration = cap_value(atoi(str[6]),0,UINT16_MAX);
-		if (str[7] != NULL) entry.isNamed = atoi(str[7]);
+#ifdef ENABLE_ITEM_GUID
+		if (str[7] != NULL) entry.GUID = atoi(str[7]);
+#endif
 		if (str[8] != NULL) entry.bound = cap_value(atoi(str[8]),BOUND_NONE,BOUND_MAX-1);
 		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))) {
 		if (!(group = (struct s_item_group_db *) uidb_get(itemdb_group, group_id))) {
 			CREATE(group, struct s_item_group_db, 1);
 			CREATE(group, struct s_item_group_db, 1);
@@ -894,6 +903,7 @@ static bool itemdb_read_nouse(char* fields[], int columns, int current) {
 * <item_id>,<flag>
 * <item_id>,<flag>
 * &1 - As dead branch item
 * &1 - As dead branch item
 * &2 - As item container
 * &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) {
 static bool itemdb_read_flag(char* fields[], int columns, int current) {
 	unsigned short nameid = atoi(fields[0]);
 	unsigned short nameid = atoi(fields[0]);
@@ -911,6 +921,9 @@ static bool itemdb_read_flag(char* fields[], int columns, int current) {
 
 
 	if (flag&1) id->flag.dead_branch = set ? 1 : 0;
 	if (flag&1) id->flag.dead_branch = set ? 1 : 0;
 	if (flag&2) id->flag.group = set ? 1 : 0;
 	if (flag&2) id->flag.group = set ? 1 : 0;
+#ifdef ENABLE_ITEM_GUID
+	if (flag&4 && itemdb_isstackable2(id)) id->flag.guid = set ? 1 : 0;
+#endif
 
 
 	return true;
 	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)
 		duration, /// Duration if item as rental item (in minutes)
 		amount; /// Amount of item will be obtained
 		amount; /// Amount of item will be obtained
 	bool isAnnounced, /// Broadcast if player get this item
 	bool isAnnounced, /// Broadcast if player get this item
+		GUID, /// Gives Unique ID for items in each box opened
 		isNamed; /// Named the item (if possible)
 		isNamed; /// Named the item (if possible)
 	char bound; /// Makes the item as bound item (according to bound type)
 	char bound; /// Makes the item as bound item (according to bound type)
 };
 };
@@ -414,6 +415,7 @@ struct item_data
 		unsigned buyingstore : 1;
 		unsigned buyingstore : 1;
 		unsigned dead_branch : 1; // As dead branch item. Logged at `branchlog` table and cannot be used at 'nobranch' mapflag [Cydh]
 		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 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;
 	} flag;
 	struct {// item stacking limitation
 	struct {// item stacking limitation
 		unsigned short amount;
 		unsigned short amount;

+ 26 - 9
src/map/pc.c

@@ -4289,13 +4289,23 @@ char pc_additem(struct map_session_data *sd,struct item *item,int amount,e_log_p
 
 
 	i = MAX_INVENTORY;
 	i = MAX_INVENTORY;
 
 
+#ifdef ENABLE_ITEM_GUID
+	if (id->flag.guid && !item->unique_id)
+		item->unique_id = pc_generate_unique_id(sd);
+#endif
+
 	// Stackable | Non Rental
 	// Stackable | Non Rental
 	if( itemdb_isstackable2(id) && item->expire_time == 0 ) {
 	if( itemdb_isstackable2(id) && item->expire_time == 0 ) {
 		for( i = 0; i < MAX_INVENTORY; i++ ) {
 		for( i = 0; i < MAX_INVENTORY; i++ ) {
 			if( sd->status.inventory[i].nameid == item->nameid &&
 			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 &&
+#ifdef ENABLE_ITEM_GUID
+				sd->status.inventory[i].unique_id == item->unique_id &&
+#endif
+				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 ) )
 				if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( id->stack.inventory && amount > id->stack.amount - sd->status.inventory[i].amount ) )
 					return ADDITEM_OVERAMOUNT;
 					return ADDITEM_OVERAMOUNT;
 				sd->status.inventory[i].amount += amount;
 				sd->status.inventory[i].amount += amount;
@@ -4324,7 +4334,7 @@ char pc_additem(struct map_session_data *sd,struct item *item,int amount,e_log_p
 		clif_additem(sd,i,amount,0);
 		clif_additem(sd,i,amount,0);
 	}
 	}
 	if( !itemdb_isstackable2(id) && !item->unique_id )
 	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]);
 	log_pick_pc(sd, log_type, amount, &sd->status.inventory[i]);
 
 
 	sd->weight += w;
 	sd->weight += w;
@@ -4858,11 +4868,17 @@ unsigned char pc_cart_additem(struct map_session_data *sd,struct item *item,int
 	i = MAX_CART;
 	i = MAX_CART;
 	if( itemdb_isstackable2(data) && !item->expire_time )
 	if( itemdb_isstackable2(data) && !item->expire_time )
 	{
 	{
-		ARR_FIND( 0, MAX_CART, i,
-			sd->status.cart[i].nameid == item->nameid && sd->status.cart[i].bound == item->bound &&
-			sd->status.cart[i].card[0] == item->card[0] && sd->status.cart[i].card[1] == item->card[1] &&
-			sd->status.cart[i].card[2] == item->card[2] && sd->status.cart[i].card[3] == item->card[3] );
-	};
+		for (i = 0; i < MAX_CART; i++) {
+			if (sd->status.cart[i].nameid == item->nameid
+				&& sd->status.cart[i].bound == item->bound
+#ifdef ENABLE_ITEM_GUID
+				&& sd->status.cart[i].unique_id == item->unique_id
+#endif
+				&& memcmp(sd->status.cart[i].card, item->card, sizeof(item->card)) == 0
+				)
+				break;
+		}
+	}
 
 
 	if( i < MAX_CART )
 	if( i < MAX_CART )
 	{// item already in cart, stack it
 	{// item already in cart, stack it
@@ -11274,6 +11290,7 @@ bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short inde
  * @return A generated Unique item ID
  * @return A generated Unique item ID
  */
  */
 uint64 pc_generate_unique_id(struct map_session_data *sd) {
 uint64 pc_generate_unique_id(struct map_session_data *sd) {
+	nullpo_ret(sd);
 	return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
 	return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
 }
 }
 
 

+ 92 - 7
src/map/script.c

@@ -6497,20 +6497,21 @@ BUILDIN_FUNC(getitem)
 	struct script_data *data;
 	struct script_data *data;
 	unsigned char flag = 0;
 	unsigned char flag = 0;
 	const char* command = script_getfuncname(st);
 	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);
 	get_val(st,data);
 	if( data_isstring(data) ) {// "<item name>"
 	if( data_isstring(data) ) {// "<item name>"
 		const char *name = conv_str(st,data);
 		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);
 			ShowError("buildin_getitem: Nonexistant item %s requested.\n", name);
 			return SCRIPT_CMD_FAILURE; //No item created.
 			return SCRIPT_CMD_FAILURE; //No item created.
 		}
 		}
-		nameid = item_data->nameid;
+		nameid = id->nameid;
 	} else if( data_isint(data) ) {// <item id>
 	} else if( data_isint(data) ) {// <item id>
 		nameid = conv_num(st,data);
 		nameid = conv_num(st,data);
-		if( !itemdb_exists(nameid) ){
+		if( !(id = itemdb_exists(nameid)) ){
 			ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
 			ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
 			return SCRIPT_CMD_FAILURE; //No item created.
 			return SCRIPT_CMD_FAILURE; //No item created.
 		}
 		}
@@ -6548,7 +6549,7 @@ BUILDIN_FUNC(getitem)
 		return SCRIPT_CMD_SUCCESS;
 		return SCRIPT_CMD_SUCCESS;
 
 
 	//Check if it's stackable.
 	//Check if it's stackable.
-	if (!itemdb_isstackable(nameid))
+	if (!itemdb_isstackable2(id))
 		get_count = 1;
 		get_count = 1;
 	else
 	else
 		get_count = amount;
 		get_count = amount;
@@ -6666,7 +6667,7 @@ BUILDIN_FUNC(getitem2)
 		item_tmp.bound = bound;
 		item_tmp.bound = bound;
 
 
 		//Check if it's stackable.
 		//Check if it's stackable.
-		if (!itemdb_isstackable(nameid))
+		if (!itemdb_isstackable2(item_data))
 			get_count = 1;
 			get_count = 1;
 		else
 		else
 			get_count = amount;
 			get_count = amount;
@@ -19111,6 +19112,89 @@ BUILDIN_FUNC(countspiritball) {
 	return SCRIPT_CMD_SUCCESS;
 	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 && memcmp(items[k].card, it->card, sizeof(it->card)) == 0) {
+					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 = 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"
 #include "../custom/script.inc"
 
 
 // declarations that were supposed to be exported from npc_chat.c
 // declarations that were supposed to be exported from npc_chat.c
@@ -19653,6 +19737,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(addspiritball,"ii?"),
 	BUILDIN_DEF(addspiritball,"ii?"),
 	BUILDIN_DEF(delspiritball,"i?"),
 	BUILDIN_DEF(delspiritball,"i?"),
 	BUILDIN_DEF(countspiritball,"?"),
 	BUILDIN_DEF(countspiritball,"?"),
+	BUILDIN_DEF(mergeitem,"?"),
 
 
 #include "../custom/script_def.inc"
 #include "../custom/script_def.inc"
 
 

+ 7 - 2
src/map/storage.c

@@ -131,7 +131,7 @@ int storage_storageopen(struct map_session_data *sd)
 }
 }
 
 
 /**
 /**
- * check if 2 item a and b have same values
+ * Check if 2 item a and b have same values
  * @param a : item 1
  * @param a : item 1
  * @param b : item 2
  * @param b : item 2
  * @return 1:same, 0:are different
  * @return 1:same, 0:are different
@@ -143,7 +143,12 @@ int compare_item(struct item *a, struct item *b)
 		a->refine == b->refine &&
 		a->refine == b->refine &&
 		a->attribute == b->attribute &&
 		a->attribute == b->attribute &&
 		a->expire_time == b->expire_time &&
 		a->expire_time == b->expire_time &&
-		a->bound == b->bound ) {
+		a->bound == b->bound
+#ifdef ENABLE_ITEM_GUID
+		&& a->unique_id == b->unique_id
+#endif
+		)
+	{
 		int i;
 		int i;
 
 
 		for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
 		for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);