Ver código fonte

* Introducing Item Reuse Limit Group
* By expanding current item_delay.txt's function to implement official item reuse limit.
* There's the 3rd optional column, put SC ID value to add reuse limit. The reuse delay will be acknowledged as status change. Ignoring this column will use 'normal' item delay.
* Added some items to their reuse limit group.
* Item DB Updates
* Added effect for ASPD_Potion (12684).
* Added effect and delay for Para_Team_Mark_ (22508).

Signed-off-by: Cydh Ramdh <cydh@pservero.com>

Cydh Ramdh 9 anos atrás
pai
commit
64cb02e5f1

+ 16 - 1
db/const.txt

@@ -1796,7 +1796,22 @@ SC_MTF_MSP	604
 SC_MTF_PUMPKIN	605
 SC_MTF_HITFLEE	606
 SC_CRIFOOD	607
-SC_EXTREMITYFIST2	608
+SC_HASTEATTACK_CASH	608
+SC_REUSE_LIMIT_A	609
+SC_REUSE_LIMIT_B	610
+SC_REUSE_LIMIT_C	611
+SC_REUSE_LIMIT_D	612
+SC_REUSE_LIMIT_E	613
+SC_REUSE_LIMIT_F	614
+SC_REUSE_LIMIT_G	615
+SC_REUSE_LIMIT_H	616
+SC_REUSE_LIMIT_MTF	617
+SC_REUSE_LIMIT_ASPD_POTION	618
+SC_REUSE_MILLENNIUMSHIELD	619
+SC_REUSE_CRUSHSTRIKE	620
+SC_REUSE_STORMBLAST	621
+SC_ALL_RIDING_REUSE_LIMIT	622
+SC_EXTREMITYFIST2	623
 
 //Status Icon
 SI_BLANK	-1

+ 45 - 20
db/pre-re/item_delay.txt

@@ -1,31 +1,56 @@
 // Item Delay Database
 //
 // Structure of Database:
-// Item ID,Delay in Milliseconds
+// <ItemID>,<Delay>{,<SC_GroupID>}
 //
-// NOTE:
-// There is a max concurrent number of entries set in src/map/itemdb.h as MAX_ITEMDELAYS.
+// <ItemID>
+//     ID of item that will has reuse delay.
+// <Delay>
+//     Re-use delay in milliseconds.
+// <SC_GroupID>
+//     SC (status change) group for the item.
+//     Example for SC_REUSE_LIMIT_MTF
+//         12658,10000,620 // Transformation Scroll(Deviruchi)
+//         12659,10000,620 // Transformation Scroll(Raydric)
+//     -> 12658 has reuse delay for 10 seconds, and also,
+//        12659 cannot be used when this delay is active.
+//     Since this is optional, default is -1 will ignores the
+//     delay group and the delay will be stored in character's
+//     data that has limit set in src/map/itemdb.h as MAX_ITEMDELAYS.
 
+// SC_REUSE_LIMIT_A 609
+14538,300000,608	//Glass_Of_Illusion
+
+// SC_REUSE_LIMIT_B 610
+14586,180000,609	//Spark_Candy
+
+// SC_REUSE_LIMIT_C 611
+12208,60000,610	//Battle_Manual
+
+// SC_REUSE_LIMIT_D 612
+12210,60000,611	//Bubble_Gum
+
+// SC_REUSE_LIMIT_E 613
+11522,1000,612	//Red_Raffle_Sap
+11523,2000,612	//Yellow_Raffle_Sap
+11524,3000,612	//White_Raffle_Sap
+
+// SC_REUSE_LIMIT_F 614
+607,5000,613	//Yggdrasil_Berry
+
+// SC_REUSE_LIMIT_G 615
+608,3000,614	//Yggdrasil_Seed
+
+// SC_REUSE_LIMIT_H 616
+11525,5000,615	//Mora_Hip_Tea
+
+// Misc
 //12202,60000	//Str_Dish10_
 //12203,60000	//Agi_Dish10_
 //12204,60000	//Int_Dish10_
 //12205,60000	//Dex_Dish10_
 //12206,60000	//Luk_Dish10_
 //12207,60000	//Vit_Dish10_
-12208,60000	//Battle_Manual
-12210,60000	//Bubble_Gum
-14538,300000	//Glass_Of_Illusion
-14586,180000	//Spark_Candy
-607,5000	//Yggdrasil_Berry
-608,3000	//Yggdrasil_Seed
-
-// Bifrost Items
-11522,1000	//Red_Raffle_Sap
-11523,2000	//Yellow_Raffle_Sap
-11524,3000	//White_Raffle_Sap
-11525,5000	//Mora_Hip_Tea
-
-// FIX ME! Delays need confirmation.
-12968,300000	//Emergency_Scroll1
-12969,300000	//Emergency_Scroll2
-12970,300000	//Emergency_Scroll3
+12968,300000	//Emergency_Scroll1 //! CHECKME: Need confirmation!
+12969,300000	//Emergency_Scroll2 //! CHECKME: Need confirmation!
+12970,300000	//Emergency_Scroll3 //! CHECKME: Need confirmation!

+ 2 - 2
db/re/item_db.txt

@@ -6556,7 +6556,7 @@
 12681,Nestea_Lemon,Nestea Lemon,2,0,,50,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12682,Nestea_Blacktea,Nestea Black Tea,2,0,,30,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12683,Sg_Vi_Potion_Box200,Siege Violet Potion Box (200),2,20,,200,,,,,0xFFFFFFFF,63,2,,,,,,{ getitem 11547,200; },{},{}
-12684,ASPD_Potion,ASPD Potion,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
+12684,ASPD_Potion,ASPD Potion,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_HASTEATTACK_CASH,900000,3; },{},{}
 12685,Gryphon_Egg_Scroll,Gryphon Egg Scroll,2,20,,10,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12686,Str_Dish20,Str Dish20,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12687,Int_Dish20,Int Dish20,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
@@ -9883,7 +9883,7 @@
 22064,Thorny_Shoes,Thorny Shoes,4,0,,1000,,25,,1,0xFFFFFFFF,63,2,64,,,1,,{ bonus bShortWeaponDamageReturn,getrefine()/2; },{},{}
 22067,Shoe_of_Witch,Shoe of Witch,4,10,,400,,10,,0,0xFFFFFFFE,63,2,64,,1,1,,{ skill "ALL_CATCRY",1,1; },{},{}
 //
-22508,Para_Team_Mark_,Eden Group Mark,11,0,,0,,,,,,,,,,,,,{},{},{}
+22508,Para_Team_Mark_,Eden Group Mark,11,0,,0,,,,0,0xFFFFFFFF,63,2,,,,,,{ /*itemskill "AL_TELEPORT",3;*/ unitskilluseid getcharid(3),"AL_TELEPORT",3; },{},{}
 22507,Worn-Out-Scroll,Worn-Out-Scroll,3,10,,10,,,,0,,,,,,,,,{},{},{}
 22510,King_Wolf_Scroll,King Wolf Scroll,2,10,,10,,,,0,0xFFFFFFFF,63,2,,,,,,{ /*TODO, confirm the rates*/ getitem callfunc("F_Rand",6635,19598,5658,6238,6239),1; },{},{}
 22513,King_of_Gift_Box,King of Gift Box,2,10,,100,,,,0,0xFFFFFFFF,63,2,,,,,,{ /*TODO, confirm the rates*/ getitem callfunc("F_Rand",12817,4403,14512),1; },{},{}

+ 80 - 30
db/re/item_delay.txt

@@ -1,41 +1,91 @@
 // Item Delay Database
 //
 // Structure of Database:
-// Item ID,Delay in Milliseconds
+// <ItemID>,<Delay>{,<SC_GroupID>}
 //
-// NOTE:
-// There is a max concurrent number of entries set in src/map/itemdb.h as MAX_ITEMDELAYS.
+// <ItemID>
+//     ID of item that will has reuse delay.
+// <Delay>
+//     Re-use delay in milliseconds.
+// <SC_GroupID>
+//     SC (status change) group for the item.
+//     Example for SC_REUSE_LIMIT_MTF
+//         12658,10000,620 // Transformation Scroll(Deviruchi)
+//         12659,10000,620 // Transformation Scroll(Raydric)
+//     -> 12658 has reuse delay for 10 seconds, and also,
+//        12659 cannot be used when this delay is active.
+//     Since this is optional, default is -1 will ignores the
+//     delay group and the delay will be stored in character's
+//     data that has limit set in src/map/itemdb.h as MAX_ITEMDELAYS.
 
+// SC_REUSE_REFRESH 317
+12725,120000,317	//Runstone_Nosiege
+
+// SC_REUSE_LIMIT_A 609
+14538,300000,608	//Glass_Of_Illusion
+
+// SC_REUSE_LIMIT_B 610
+12596,180000,609	//Magic_Candy
+14586,180000,609	//Spark_Candy
+
+// SC_REUSE_LIMIT_C 611
+12208,60000,610	//Battle_Manual
+
+// SC_REUSE_LIMIT_D 612
+12210,60000,611	//Bubble_Gum
+
+// SC_REUSE_LIMIT_E 613
+11522,1000,612	//Red_Raffle_Sap
+11523,2000,612	//Yellow_Raffle_Sap
+11524,3000,612	//White_Raffle_Sap
+
+// SC_REUSE_LIMIT_F 614
+607,5000,613	//Yggdrasil_Berry
+
+// SC_REUSE_LIMIT_G 615
+608,3000,614	//Yggdrasil_Seed
+22559,3000,614	//Mock_Strawberry
+
+// SC_REUSE_LIMIT_H 616
+11525,5000,615	//Mora_Hip_Tea
+
+// SC_REUSE_LIMIT_ASPD_POTION 617
+//12684,0,616	//ASPD_Potion //! CHECKME: Need confirmation!
+
+// SC_REUSE_LIMIT_MTF 618
+12658,10000,620	//Transformation Scroll(Deviruchi)
+12659,10000,620	//Transformation Scroll(Raydric)
+12660,10000,620	//Transformation Scroll(Mavka)
+12661,10000,620	//Transformation Scroll(Marduk)
+12662,10000,620	//Transformation Scroll(Banshee)
+12663,10000,620	//Transformation Scroll(Poring)
+12664,10000,620	//Transformation Scroll(Golem)
+
+// SC_ALL_RIDING_REUSE_LIMIT 619
+12622,3000,621	//Boarding_Halter
+
+// SC_SEARCH_STORE_INFO 620
+12580,0,624	//Vending_Search_Scroll
+12581,0,624	//Vending_Search_Scroll2
+12591,0,624	//Uni_Catalog_Bz
+
+// SC_REUSE_MILLENNIUMSHIELD 621
+12727,60000,625	//Runstone_Verkana
+
+// SC_REUSE_CRUSHSTRIKE 622
+12726,30000,626	//Runstone_Rhydo
+
+// SC_REUSE_STORMBLAST 623
+12732,1000,627	//Runstone_Pertz
+
+// Misc
 //12202,60000	//Str_Dish10_
 //12203,60000	//Agi_Dish10_
 //12204,60000	//Int_Dish10_
 //12205,60000	//Dex_Dish10_
 //12206,60000	//Luk_Dish10_
 //12207,60000	//Vit_Dish10_
-12208,60000	//Battle_Manual
-12210,60000	//Bubble_Gum
-14538,300000	//Glass_Of_Illusion
-607,5000	//Yggdrasil_Berry
-608,3000	//Yggdrasil_Seed
-
-// Bifrost Items
-11522,1000	//Red_Raffle_Sap
-11523,2000	//Yellow_Raffle_Sap
-11524,3000	//White_Raffle_Sap
-11525,5000	//Mora_Hip_Tea
-
-12658,10000	//Transformation Scroll(Deviruchi)
-12659,10000	//Transformation Scroll(Raydric)
-12660,10000	//Transformation Scroll(Mavka)
-12661,10000	//Transformation Scroll(Marduk)
-12662,10000	//Transformation Scroll(Banshee)
-12663,10000	//Transformation Scroll(Poring)
-12664,10000	//Transformation Scroll(Golem)
-
-// FIX ME! Delays need confirmation.
-12968,300000	//Emergency_Scroll1
-12969,300000	//Emergency_Scroll2
-12970,300000	//Emergency_Scroll3
-
-12596,300000	//Magic_Candy
-14586,300000	//Spark_Candy
+12968,300000	//Emergency_Scroll1 //! CHECKME: Need confirmation!
+12969,300000	//Emergency_Scroll2 //! CHECKME: Need confirmation!
+12970,300000	//Emergency_Scroll3 //! CHECKME: Need confirmation!
+22508,7200000	//Para_Team_Mark_

+ 2 - 2
sql-files/item_db_re.sql

@@ -6587,7 +6587,7 @@ REPLACE INTO `item_db_re` VALUES (12680,'Sg_Blue_Potion_Box','Siege Blue Potion
 REPLACE INTO `item_db_re` VALUES (12681,'Nestea_Lemon','Nestea Lemon',2,0,NULL,50,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12682,'Nestea_Blacktea','Nestea Black Tea',2,0,NULL,30,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12683,'Sg_Vi_Potion_Box200','Siege Violet Potion Box (200)',2,20,NULL,200,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'getitem 11547,200;',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (12684,'ASPD_Potion','ASPD Potion',2,0,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (12684,'ASPD_Potion','ASPD Potion',2,0,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_HASTEATTACK_CASH,900000,3;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12685,'Gryphon_Egg_Scroll','Gryphon Egg Scroll',2,20,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12686,'Str_Dish20','Str Dish20',2,0,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (12687,'Int_Dish20','Int Dish20',2,0,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
@@ -9914,7 +9914,7 @@ REPLACE INTO `item_db_re` VALUES (22059,'Aegir_Shoes','Aegir Shoes',4,10,NULL,30
 REPLACE INTO `item_db_re` VALUES (22064,'Thorny_Shoes','Thorny Shoes',4,0,NULL,1000,NULL,25,NULL,1,0xFFFFFFFF,63,2,64,NULL,NULL,1,NULL,'bonus bShortWeaponDamageReturn,getrefine()/2;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (22067,'Shoe_of_Witch','Shoe of Witch',4,10,NULL,400,NULL,10,NULL,0,0xFFFFFFFE,63,2,64,NULL,'1',1,NULL,'skill "ALL_CATCRY",1,1;',NULL,NULL);
 #
-REPLACE INTO `item_db_re` VALUES (22508,'Para_Team_Mark_','Eden Group Mark',11,0,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (22508,'Para_Team_Mark_','Eden Group Mark',11,0,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'/*itemskill "AL_TELEPORT",3;*/ unitskilluseid getcharid(3),"AL_TELEPORT",3;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (22507,'Worn-Out-Scroll','Worn-Out-Scroll',3,10,NULL,10,NULL,NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (22510,'King_Wolf_Scroll','King Wolf Scroll',2,10,NULL,10,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'/*TODO, confirm the rates*/ getitem callfunc("F_Rand",6635,19598,5658,6238,6239),1;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (22513,'King_of_Gift_Box','King of Gift Box',2,10,NULL,100,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'/*TODO, confirm the rates*/ getitem callfunc("F_Rand",12817,4403,14512),1;',NULL,NULL);

+ 1 - 0
src/map/clif.h

@@ -428,6 +428,7 @@ enum clif_messages {
 	ITEM_PRODUCE_SUCCESS = 0x627,
 	ITEM_PRODUCE_FAIL = 0x628,
 	ITEM_UNIDENTIFIED = 0x62d,
+	ITEM_REUSE_LIMIT = 0x746,
 	USAGE_FAIL = 0x783,
 	NEED_REINS_OF_MOUNT = 0x78c,
 };

+ 6 - 1
src/map/itemdb.c

@@ -786,6 +786,11 @@ static bool itemdb_read_itemdelay(char* str[], int columns, int current) {
 
 	id->delay = delay;
 
+	if (columns == 2)
+		id->delay_sc = SC_NONE;
+	else
+		id->delay_sc = (sc_type)atoi(str[2]);
+
 	return true;
 }
 
@@ -1602,7 +1607,7 @@ static void itemdb_read(void) {
 		itemdb_read_combos(dbsubpath2,i); //TODO change this to sv_read ? id#script ?
 		sv_readdb(dbsubpath2, "item_noequip.txt",       ',', 2, 2, -1, &itemdb_read_noequip, i);
 		sv_readdb(dbsubpath2, "item_trade.txt",         ',', 3, 3, -1, &itemdb_read_itemtrade, i);
-		sv_readdb(dbsubpath2, "item_delay.txt",         ',', 2, 2, -1, &itemdb_read_itemdelay, i);
+		sv_readdb(dbsubpath2, "item_delay.txt",         ',', 2, 3, -1, &itemdb_read_itemdelay, i);
 		sv_readdb(dbsubpath2, "item_buyingstore.txt",   ',', 1, 1, -1, &itemdb_read_buyingstore, i);
 		sv_readdb(dbsubpath2, "item_flag.txt",          ',', 2, 2, -1, &itemdb_read_flag, i);
 		aFree(dbsubpath1);

+ 1 - 0
src/map/itemdb.h

@@ -450,6 +450,7 @@ struct item_data
 	/* bugreport:309 */
 	struct item_combo **combos;
 	unsigned char combos_count;
+	enum sc_type delay_sc; ///< Use delay group if any instead using player's item_delay data [Cydh]
 };
 
 struct item_data* itemdb_searchname(const char *name);

+ 80 - 36
src/map/pc.c

@@ -4768,41 +4768,8 @@ int pc_useitem(struct map_session_data *sd,int n)
 	if( id->flag.delay_consume && ( sd->ud.skilltimer != INVALID_TIMER /*|| !status_check_skilluse(&sd->bl, &sd->bl, ALL_RESURRECTION, 0)*/ ) )
 		return 0;
 
-	if( id->delay > 0 && !pc_has_permission(sd,PC_PERM_ITEM_UNCONDITIONAL) ) {
-		int i;
-		ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == nameid );
-			if( i == MAX_ITEMDELAYS ) /* item not found. try first empty now */
-				ARR_FIND(0, MAX_ITEMDELAYS, i, !sd->item_delay[i].nameid );
-		if( i < MAX_ITEMDELAYS ) {
-			if( sd->item_delay[i].nameid ) {// found
-				if( DIFF_TICK(sd->item_delay[i].tick, tick) > 0 ) {
-					int e_tick = DIFF_TICK(sd->item_delay[i].tick, tick)/1000;
-					char e_msg[CHAT_SIZE_MAX];
-					if( e_tick > 99 )
-						sprintf(e_msg,msg_txt(sd,379), // Item Failed. [%s] is cooling down. Wait %.1f minutes.
-										itemdb_jname(sd->item_delay[i].nameid), (double)e_tick / 60);
-					else
-						sprintf(e_msg,msg_txt(sd,380), // Item Failed. [%s] is cooling down. Wait %d seconds.
-										itemdb_jname(sd->item_delay[i].nameid), e_tick+1);
-					clif_colormes(sd->fd,color_table[COLOR_YELLOW],e_msg);
-					return 0; // Delay has not expired yet
-				}
-			} else {// not yet used item (all slots are initially empty)
-				sd->item_delay[i].nameid = nameid;
-			}
-			if( !(nameid == ITEMID_REINS_OF_MOUNT && sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR)) )
-				sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay;
-		} else {// should not happen
-			ShowError("pc_useitem: Exceeded item delay array capacity! (nameid=%hu, char_id=%d)\n", nameid, sd->status.char_id);
-		}
-		//clean up used delays so we can give room for more
-		for(i = 0; i < MAX_ITEMDELAYS; i++) {
-			if( DIFF_TICK(sd->item_delay[i].tick, tick) <= 0 ) {
-				sd->item_delay[i].tick = 0;
-				sd->item_delay[i].nameid = 0;
-			}
-		}
-	}
+	if( id->delay > 0 && !pc_has_permission(sd,PC_PERM_ITEM_UNCONDITIONAL) && pc_itemcd_check(sd, id, tick, n))
+		return 0;
 
 	/* on restricted maps the item is consumed but the effect is not used */
 	if (!pc_has_permission(sd,PC_PERM_ITEM_UNCONDITIONAL) && itemdb_isNoEquip(id,sd->bl.m)) {
@@ -10967,13 +10934,14 @@ int pc_read_motd(void)
 
 	return 0;
 }
+
 void pc_itemcd_do(struct map_session_data *sd, bool load) {
 	int i,cursor = 0;
 	struct item_cd* cd = NULL;
 
 	if( load ) {
 		if( !(cd = idb_get(itemcd_db, sd->status.char_id)) ) {
-			// no skill cooldown is associated with this character
+			// no item cooldown is associated with this character
 			return;
 		}
 		for(i = 0; i < MAX_ITEMDELAYS; i++) {
@@ -11001,6 +10969,82 @@ void pc_itemcd_do(struct map_session_data *sd, bool load) {
 	return;
 }
 
+/**
+ * Add item delay to player's item delay data
+ * @param sd Player
+ * @param id Item data
+ * @param tick Current tick
+ * @param n Item index in inventory
+ * @return 0: No delay, can consume item.
+ *         1: Has delay, cancel consumption.
+ **/
+uint8 pc_itemcd_add(struct map_session_data *sd, struct item_data *id, unsigned int tick, unsigned short n) {
+	int i;
+	ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == id->nameid );
+	if( i == MAX_ITEMDELAYS ) /* item not found. try first empty now */
+		ARR_FIND(0, MAX_ITEMDELAYS, i, !sd->item_delay[i].nameid );
+	if( i < MAX_ITEMDELAYS ) {
+		if( sd->item_delay[i].nameid ) {// found
+			if( DIFF_TICK(sd->item_delay[i].tick, tick) > 0 ) {
+				int e_tick = DIFF_TICK(sd->item_delay[i].tick, tick)/1000;
+				char e_msg[CHAT_SIZE_MAX];
+				if( e_tick > 99 )
+					sprintf(e_msg,msg_txt(sd,379), // Item Failed. [%s] is cooling down. Wait %.1f minutes.
+									itemdb_jname(sd->item_delay[i].nameid), (double)e_tick / 60);
+				else
+					sprintf(e_msg,msg_txt(sd,380), // Item Failed. [%s] is cooling down. Wait %d seconds.
+									itemdb_jname(sd->item_delay[i].nameid), e_tick+1);
+				clif_colormes(sd->fd,color_table[COLOR_YELLOW],e_msg);
+				return 1; // Delay has not expired yet
+			}
+		} else {// not yet used item (all slots are initially empty)
+			sd->item_delay[i].nameid = id->nameid;
+		}
+		if( !(id->nameid == ITEMID_REINS_OF_MOUNT && sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR)) )
+			sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay;
+	} else {// should not happen
+		ShowError("pc_itemcd_add: Exceeded item delay array capacity! (nameid=%hu, char_id=%d)\n", id->nameid, sd->status.char_id);
+	}
+	//clean up used delays so we can give room for more
+	for(i = 0; i < MAX_ITEMDELAYS; i++) {
+		if( DIFF_TICK(sd->item_delay[i].tick, tick) <= 0 ) {
+			sd->item_delay[i].tick = 0;
+			sd->item_delay[i].nameid = 0;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Check if player has delay to reuse item
+ * @param sd Player
+ * @param id Item data
+ * @param tick Current tick
+ * @param n Item index in inventory
+ * @return 0: No delay, can consume item.
+ *         1: Has delay, cancel consumption.
+ **/
+uint8 pc_itemcd_check(struct map_session_data *sd, struct item_data *id, unsigned int tick, unsigned short n) {
+	struct status_change *sc = NULL;
+
+	nullpo_retr(0, sd);
+	nullpo_retr(0, id);
+
+	// Do normal delay assignment
+	if (id->delay_sc <= SC_NONE || id->delay_sc >= SC_MAX || !(sc = &sd->sc))
+		return pc_itemcd_add(sd, id, tick, n);
+
+	// Send reply of delay remains
+	if (sc->data[id->delay_sc]) {
+		const struct TimerData *timer = get_timer(sc->data[id->delay_sc]->timer);
+		clif_msg_value(sd, ITEM_REUSE_LIMIT, timer ? DIFF_TICK(timer->tick, tick) / 1000 : 99);
+		return 1;
+	}
+
+	sc_start(&sd->bl, &sd->bl, id->delay_sc, 100, id->nameid, id->delay);
+	return 0;
+}
+
 /**
 * Clear the dmglog data from player
 * @param sd

+ 2 - 0
src/map/pc.h

@@ -1153,6 +1153,8 @@ void pc_overheat(struct map_session_data *sd, int val);
 int pc_banding(struct map_session_data *sd, uint16 skill_lv);
 
 void pc_itemcd_do(struct map_session_data *sd, bool load);
+uint8 pc_itemcd_add(struct map_session_data *sd, struct item_data *id, unsigned int tick, unsigned short n);
+uint8 pc_itemcd_check(struct map_session_data *sd, struct item_data *id, unsigned int tick, unsigned short n);
 
 int pc_load_combo(struct map_session_data *sd);
 

+ 6 - 0
src/map/script.c

@@ -10575,6 +10575,12 @@ BUILDIN_FUNC(sc_end)
 			case SC_MTF_MSP:
 			case SC_MTF_PUMPKIN:
 			case SC_MTF_HITFLEE:
+			case SC_ATTHASTE_CASH:
+			case SC_REUSE_LIMIT_A:				case SC_REUSE_LIMIT_B:			case SC_REUSE_LIMIT_C:
+			case SC_REUSE_LIMIT_D:				case SC_REUSE_LIMIT_E:			case SC_REUSE_LIMIT_F:
+			case SC_REUSE_LIMIT_G:				case SC_REUSE_LIMIT_H:			case SC_REUSE_LIMIT_MTF:
+			case SC_REUSE_LIMIT_ASPD_POTION:	case SC_REUSE_MILLENNIUMSHIELD:	case SC_REUSE_CRUSHSTRIKE:
+			case SC_REUSE_STORMBLAST:			case SC_ALL_RIDING_REUSE_LIMIT:	case SC_REUSE_REFRESH:
 				return 0;
 			default:
 				break;

+ 18 - 0
src/map/skill.c

@@ -1700,6 +1700,12 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
 					case SC_MTF_ASPD2:		case SC_MTF_RANGEATK2:	case SC_MTF_MATK2:
 					case SC_2011RWC_SCROLL:		case SC_JP_EVENT04:	case SC_MTF_MHP:
 					case SC_MTF_MSP:		case SC_MTF_PUMPKIN:	case SC_MTF_HITFLEE:
+					case SC_ATTHASTE_CASH:	case SC_REUSE_REFRESH:
+					case SC_REUSE_LIMIT_A:	case SC_REUSE_LIMIT_B:	case SC_REUSE_LIMIT_C:
+					case SC_REUSE_LIMIT_D:	case SC_REUSE_LIMIT_E:	case SC_REUSE_LIMIT_F:
+					case SC_REUSE_LIMIT_G:	case SC_REUSE_LIMIT_H:	case SC_REUSE_LIMIT_MTF:
+					case SC_REUSE_LIMIT_ASPD_POTION:	case SC_REUSE_MILLENNIUMSHIELD:	case SC_REUSE_CRUSHSTRIKE:
+					case SC_REUSE_STORMBLAST:	case SC_ALL_RIDING_REUSE_LIMIT:
 						continue;
 					case SC_WHISTLE:		case SC_ASSNCROS:		case SC_POEMBRAGI:
 					case SC_APPLEIDUN:		case SC_HUMMING:		case SC_DONTFORGETME:
@@ -7440,6 +7446,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					case SC_MTF_ASPD2:		case SC_MTF_RANGEATK2:	case SC_MTF_MATK2:
 					case SC_2011RWC_SCROLL:		case SC_JP_EVENT04:	case SC_MTF_MHP:
 					case SC_MTF_MSP:		case SC_MTF_PUMPKIN:	case SC_MTF_HITFLEE:
+					case SC_ATTHASTE_CASH:	case SC_REUSE_REFRESH:
+					case SC_REUSE_LIMIT_A:	case SC_REUSE_LIMIT_B:	case SC_REUSE_LIMIT_C:
+					case SC_REUSE_LIMIT_D:	case SC_REUSE_LIMIT_E:	case SC_REUSE_LIMIT_F:
+					case SC_REUSE_LIMIT_G:	case SC_REUSE_LIMIT_H:	case SC_REUSE_LIMIT_MTF:
+					case SC_REUSE_LIMIT_ASPD_POTION:	case SC_REUSE_MILLENNIUMSHIELD:	case SC_REUSE_CRUSHSTRIKE:
+					case SC_REUSE_STORMBLAST:	case SC_ALL_RIDING_REUSE_LIMIT:
 						continue;
 					//bugreport:4888 these songs may only be dispelled if you're not in their song area anymore
 					case SC_WHISTLE:
@@ -8922,6 +8934,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					case SC_MTF_ASPD2:		case SC_MTF_RANGEATK2:	case SC_MTF_MATK2:
 					case SC_2011RWC_SCROLL:		case SC_JP_EVENT04:	case SC_MTF_MHP:
 					case SC_MTF_MSP:		case SC_MTF_PUMPKIN:	case SC_MTF_HITFLEE:
+					case SC_ATTHASTE_CASH:	case SC_REUSE_REFRESH:
+					case SC_REUSE_LIMIT_A:	case SC_REUSE_LIMIT_B:	case SC_REUSE_LIMIT_C:
+					case SC_REUSE_LIMIT_D:	case SC_REUSE_LIMIT_E:	case SC_REUSE_LIMIT_F:
+					case SC_REUSE_LIMIT_G:	case SC_REUSE_LIMIT_H:	case SC_REUSE_LIMIT_MTF:
+					case SC_REUSE_LIMIT_ASPD_POTION:	case SC_REUSE_MILLENNIUMSHIELD:	case SC_REUSE_CRUSHSTRIKE:
+					case SC_REUSE_STORMBLAST:	case SC_ALL_RIDING_REUSE_LIMIT:
 					continue;
 				case SC_ASSUMPTIO:
 					if( bl->type == BL_MOB )

+ 77 - 0
src/map/status.c

@@ -886,6 +886,7 @@ void initChangeTables(void)
 	StatusIconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION;
 	StatusIconChangeTable[SC_SPCOST_RATE] = SI_ATKER_BLOOD;
 	StatusIconChangeTable[SC_COMMONSC_RESIST] = SI_TARGET_BLOOD;
+	StatusIconChangeTable[SC_ATTHASTE_CASH] = SI_ATTHASTE_CASH;
 
 	/* Mercenary Bonus Effects */
 	StatusIconChangeTable[SC_MERC_FLEEUP] = SI_MERC_FLEEUP;
@@ -1007,6 +1008,23 @@ void initChangeTables(void)
 	StatusIconChangeTable[SC_MTF_MSP] = SI_MTF_MSP;
 	StatusIconChangeTable[SC_MTF_PUMPKIN] = SI_MTF_PUMPKIN;
 
+	// Item Reuse Limits
+	StatusIconChangeTable[SC_REUSE_REFRESH] = SI_REUSE_REFRESH;
+	StatusIconChangeTable[SC_REUSE_LIMIT_A] = SI_REUSE_LIMIT_A;
+	StatusIconChangeTable[SC_REUSE_LIMIT_B] = SI_REUSE_LIMIT_B;
+	StatusIconChangeTable[SC_REUSE_LIMIT_C] = SI_REUSE_LIMIT_C;
+	StatusIconChangeTable[SC_REUSE_LIMIT_D] = SI_REUSE_LIMIT_D;
+	StatusIconChangeTable[SC_REUSE_LIMIT_E] = SI_REUSE_LIMIT_E;
+	StatusIconChangeTable[SC_REUSE_LIMIT_F] = SI_REUSE_LIMIT_F;
+	StatusIconChangeTable[SC_REUSE_LIMIT_G] = SI_REUSE_LIMIT_G;
+	StatusIconChangeTable[SC_REUSE_LIMIT_H] = SI_REUSE_LIMIT_H;
+	StatusIconChangeTable[SC_REUSE_LIMIT_MTF] = SI_REUSE_LIMIT_MTF;
+	StatusIconChangeTable[SC_REUSE_LIMIT_ASPD_POTION] = SI_REUSE_LIMIT_ASPD_POTION;
+	StatusIconChangeTable[SC_REUSE_MILLENNIUMSHIELD] = SI_REUSE_MILLENNIUMSHIELD;
+	StatusIconChangeTable[SC_REUSE_CRUSHSTRIKE] = SI_REUSE_CRUSHSTRIKE;
+	StatusIconChangeTable[SC_REUSE_STORMBLAST] = SI_REUSE_STORMBLAST;
+	StatusIconChangeTable[SC_ALL_RIDING_REUSE_LIMIT] = SI_ALL_RIDING_REUSE_LIMIT;
+
 	/* Other SC which are not necessarily associated to skills */
 	StatusChangeFlagTable[SC_ASPDPOTION0] |= SCB_ASPD;
 	StatusChangeFlagTable[SC_ASPDPOTION1] |= SCB_ASPD;
@@ -1064,6 +1082,7 @@ void initChangeTables(void)
 	StatusChangeFlagTable[SC_FOOD_DEX_CASH] |= SCB_DEX;
 	StatusChangeFlagTable[SC_FOOD_INT_CASH] |= SCB_INT;
 	StatusChangeFlagTable[SC_FOOD_LUK_CASH] |= SCB_LUK;
+	StatusChangeFlagTable[SC_ATTHASTE_CASH] |= SCB_ASPD;
 
 	/* Mercenary Bonus Effects */
 	StatusChangeFlagTable[SC_MERC_FLEEUP] |= SCB_FLEE;
@@ -6255,6 +6274,9 @@ static short status_calc_aspd(struct block_list *bl, struct status_change *sc, s
 		sc->data[i=SC_ASPDPOTION0])
 		pots += sc->data[i]->val1;
 
+	if (sc->data[SC_ATTHASTE_CASH])
+		pots += sc->data[SC_ATTHASTE_CASH]->val1;
+
 	if( !sc->data[SC_QUAGMIRE] ) {
 		if(sc->data[SC_TWOHANDQUICKEN] && skills1 < 7)
 			skills1 = 7;
@@ -6478,6 +6500,9 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change *
 		sc->data[i=SC_ASPDPOTION0] )
 		aspd_rate -= sc->data[i]->val2;
 
+	if (sc->data[SC_ATTHASTE_CASH])
+		aspd_rate -= sc->data[SC_ATTHASTE_CASH]->val2;
+
 	if(sc->data[SC_DONTFORGETME])
 		aspd_rate += sc->data[SC_DONTFORGETME]->val2;
 	if(sc->data[SC_LONGING])
@@ -8539,6 +8564,21 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			case SC__WEAKNESS:
 			case SC__UNLUCKY:
 			//case SC__CHAOS:
+			case SC_REUSE_REFRESH:
+			case SC_REUSE_LIMIT_A:
+			case SC_REUSE_LIMIT_B:
+			case SC_REUSE_LIMIT_C:
+			case SC_REUSE_LIMIT_D:
+			case SC_REUSE_LIMIT_E:
+			case SC_REUSE_LIMIT_F:
+			case SC_REUSE_LIMIT_G:
+			case SC_REUSE_LIMIT_H:
+			case SC_REUSE_LIMIT_MTF:
+			case SC_REUSE_LIMIT_ASPD_POTION:
+			case SC_REUSE_MILLENNIUMSHIELD:
+			case SC_REUSE_CRUSHSTRIKE:
+			case SC_REUSE_STORMBLAST:
+			case SC_ALL_RIDING_REUSE_LIMIT:
 				return 0;
 			case SC_COMBO:
 			case SC_DANCING:
@@ -8552,6 +8592,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			case SC_ENCHANTARMS:
 			case SC_ARMOR_ELEMENT:
 			case SC_ARMOR_RESIST:
+			case SC_ATTHASTE_CASH:
 				break;
 			case SC_GOSPEL:
 				 // Must not override a casting gospel char.
@@ -8841,6 +8882,10 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			val2 = 50*(2+type-SC_ASPDPOTION0);
 			break;
 
+		case SC_ATTHASTE_CASH:
+			val2 = 50*val1; // Just custom for pre-re
+			break;
+
 		case SC_NOCHAT:
 			// A hardcoded interval of 60 seconds is expected, as the time that SC_NOCHAT uses is defined by
 			// mmocharstatus.manner, each negative point results in 1 minute with this status activated.
@@ -10771,6 +10816,22 @@ int status_change_clear(struct block_list* bl, int type)
 			case SC_QUEST_BUFF3:
 			case SC_2011RWC_SCROLL:
 			case SC_JP_EVENT04:
+			case SC_ATTHASTE_CASH:
+			case SC_REUSE_REFRESH:
+			case SC_REUSE_LIMIT_A:
+			case SC_REUSE_LIMIT_B:
+			case SC_REUSE_LIMIT_C:
+			case SC_REUSE_LIMIT_D:
+			case SC_REUSE_LIMIT_E:
+			case SC_REUSE_LIMIT_F:
+			case SC_REUSE_LIMIT_G:
+			case SC_REUSE_LIMIT_H:
+			case SC_REUSE_LIMIT_MTF:
+			case SC_REUSE_LIMIT_ASPD_POTION:
+			case SC_REUSE_MILLENNIUMSHIELD:
+			case SC_REUSE_CRUSHSTRIKE:
+			case SC_REUSE_STORMBLAST:
+			case SC_ALL_RIDING_REUSE_LIMIT:
 				continue;
 			}
 		}
@@ -12652,6 +12713,22 @@ void status_change_clear_buffs (struct block_list* bl, int type)
 			case SC_MTF_MSP:
 			case SC_MTF_PUMPKIN:
 			case SC_MTF_HITFLEE:
+			case SC_ATTHASTE_CASH:
+			case SC_REUSE_REFRESH:
+			case SC_REUSE_LIMIT_A:
+			case SC_REUSE_LIMIT_B:
+			case SC_REUSE_LIMIT_C:
+			case SC_REUSE_LIMIT_D:
+			case SC_REUSE_LIMIT_E:
+			case SC_REUSE_LIMIT_F:
+			case SC_REUSE_LIMIT_G:
+			case SC_REUSE_LIMIT_H:
+			case SC_REUSE_LIMIT_MTF:
+			case SC_REUSE_LIMIT_ASPD_POTION:
+			case SC_REUSE_MILLENNIUMSHIELD:
+			case SC_REUSE_CRUSHSTRIKE:
+			case SC_REUSE_STORMBLAST:
+			case SC_ALL_RIDING_REUSE_LIMIT:
 				continue;
 
 			// Debuffs that can be removed.

+ 23 - 6
src/map/status.h

@@ -716,6 +716,23 @@ typedef enum sc_type {
 	SC_MTF_HITFLEE,
 
 	SC_CRIFOOD,
+	SC_ATTHASTE_CASH,
+
+	// Item Reuse Limits
+	SC_REUSE_LIMIT_A,
+	SC_REUSE_LIMIT_B,
+	SC_REUSE_LIMIT_C,
+	SC_REUSE_LIMIT_D,
+	SC_REUSE_LIMIT_E,
+	SC_REUSE_LIMIT_F,
+	SC_REUSE_LIMIT_G,
+	SC_REUSE_LIMIT_H,
+	SC_REUSE_LIMIT_MTF,
+	SC_REUSE_LIMIT_ASPD_POTION,
+	SC_REUSE_MILLENNIUMSHIELD,
+	SC_REUSE_CRUSHSTRIKE,
+	SC_REUSE_STORMBLAST,
+	SC_ALL_RIDING_REUSE_LIMIT,
 
 #ifdef RENEWAL
 	SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled
@@ -1029,14 +1046,14 @@ enum si_type {
 	SI_ATKER_BLOOD = 300,
 	SI_TARGET_BLOOD = 301,
 	SI_ARMOR_PROPERTY = 302,
-//	SI_REUSE_LIMIT_A = 303,
+	SI_REUSE_LIMIT_A = 303,
 	SI_HELLPOWER = 304,
 	SI_STEAMPACK = 305,
-//	SI_REUSE_LIMIT_B = 306,
-//	SI_REUSE_LIMIT_C = 307,
-//	SI_REUSE_LIMIT_D = 308,
-//	SI_REUSE_LIMIT_E = 309,
-//	SI_REUSE_LIMIT_F = 310,
+	SI_REUSE_LIMIT_B = 306,
+	SI_REUSE_LIMIT_C = 307,
+	SI_REUSE_LIMIT_D = 308,
+	SI_REUSE_LIMIT_E = 309,
+	SI_REUSE_LIMIT_F = 310,
 	SI_INVINCIBLE = 311,
 	SI_CASH_PLUSONLYJOBEXP = 312,
 	SI_PARTYFLEE = 313,