Browse Source

Clean up something RENEWAL_CAST
* Moved additive bonuses (reducing/increasing) to first calculation before the rate adjustment
* Corrected bFixedCastrate, shouldn't be stacked. Example between Puente_Robe (15012) is -3% and +10 Rafini_Staff (1649) is -10%, only -10% will be used not -13%.
* Reversed some value assignment, `-=` to `+=` and other part that affected by this changes.
* Also as follow up c3e488e & 4f4d8fe, fixed `bonus2 bFixedCastrate,"sk",rate;`
* Corrected `bFixedCastrate` for Krieger_Knuckle2 (1827) only for skill MO_EXTREMITYFIST
* Moved default the 20% of fixed cast rate to conf/battle/skill.conf `default_fixed_castrate`

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

Cydh Ramdh 10 năm trước cách đây
mục cha
commit
5c6afd7ea3
12 tập tin đã thay đổi với 224 bổ sung176 xóa
  1. 6 0
      conf/battle/skill.conf
  2. 1 1
      db/re/item_db.txt
  3. 12 10
      doc/item_bonus.txt
  4. 1 1
      sql-files/item_db_re.sql
  5. 6 4
      src/config/const.h
  6. 24 21
      src/config/renewal.h
  7. 1 0
      src/map/battle.c
  8. 1 0
      src/map/battle.h
  9. 73 72
      src/map/pc.c
  10. 3 3
      src/map/pc.h
  11. 95 63
      src/map/skill.c
  12. 1 1
      src/map/status.c

+ 6 - 0
conf/battle/skill.conf

@@ -330,3 +330,9 @@ arrow_shower_knockback: yes
 // punch a hole into SG it will for example create a "suck in" effect.
 // If you disable this setting, the knockback direction will be completely random (eAthena style).
 stormgust_knockback: yes
+
+// For RENEWAL_CAST (Note 2)
+// By default skill that has '0' value for Fixed Casting Time will use 20% of cast time
+// as Fixed Casting Time, and the rest (80%) as Variable Casting Time.
+// Put it 0 to disable default Fixed Casting Time (just like -1 is the skill_cast_db.txt).
+default_fixed_castrate: 20

+ 1 - 1
db/re/item_db.txt

@@ -1080,7 +1080,7 @@
 1824,BF_Knuckle2,Brave Battle Fist,5,20,,0,30,,1,0,0x00008100,63,2,2,3,80,1,12,{ bonus bStr,2; bonus bInt,1; bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-25; autobonus "{ bonus2 bVariableCastrate,\"MO_EXTREMITYFIST\",-100; }",50,6000,BF_WEAPON,"{ specialeffect2 EF_SUFFRAGIUM; }"; bonus bUnbreakableWeapon,0; },{},{}
 1825,Horn_Of_Hilthrion,Horn of Hillslion,5,20,,600,95,,1,3,0x00008000,18,2,2,3,60,1,12,{ bonus3 bAutoSpell,"NPC_CRITICALWOUND",1,100; bonus4 bAutoSpellOnSkill,"CH_PALMSTRIKE","MO_INVESTIGATE",1,100; bonus3 bAutoSpell,"MO_CALLSPIRITS",5,100; },{},{}
 1826,Krieger_Knuckle1,Glorious Claw,5,20,,0,30,,1,0,0x00008100,63,2,2,4,80,1,12,{ bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus3 bAutoSpell,"MO_INVESTIGATE",5,(getrefine()*10-50); bonus3 bAutoSpell,"AL_DECAGI",1,(getrefine()*10-50); } },{},{}
-1827,Krieger_Knuckle2,Glorious Fist,5,20,,0,30,,1,0,0x00008100,63,2,2,4,80,1,12,{ bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-100; bonus4 bautospellonskill,"MO_EXPLOSIONSPIRITS","CH_SOULCOLLECT",1,1000; bonus bFixedCastrate,-100; } },{},{}
+1827,Krieger_Knuckle2,Glorious Fist,5,20,,0,30,,1,0,0x00008100,63,2,2,4,80,1,12,{ bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-100; bonus2 bFixedCastrate,"MO_EXTREMITYFIST",-100; bonus4 bautospellonskill,"MO_EXPLOSIONSPIRITS","CH_SOULCOLLECT",1,1000; } },{},{}
 1828,Monk_Knuckle,Monk Knuckle,5,20,,0,150,,1,0,0x00008100,63,2,2,4,0,0,12,{ bonus bInt,2; bonus2 bSkillAtk,"MO_FINGEROFFENSIVE",25; },{},{}
 1829,Fist_C,Fist,5,0,,0,150,,1,0,0x00008100,63,2,2,3,1,0,12,{ bonus2 bAddSize,Size_All,40; },{},{}
 1830,Sura_Rampage,Sura Rampage,5,20,,500,142,,1,1,0x00008100,63,2,2,3,102,1,12,{ bonus2 bSkillAtk,"SR_EARTHSHAKER",20; bonus2 bSkillAtk,"SR_SKYNETBLOW",20; bonus bUseSPrate,5; if(getrefine()>6) { bonus bUseSPrate,-1*(getrefine()-6); } },{},{}

+ 12 - 10
doc/item_bonus.txt

@@ -174,16 +174,18 @@ bonus2 bAddItemGroupHealRate,ig,n;	Increases HP recovered by n% for items of ite
 
 Cast time/delay
 ---------------
-bonus bCastrate,n;             		Skill cast time rate + n%
-bonus2 bCastrate,sk,n;         		Adjust casting time of skill sk by n%
-bonus bFixedCastrate,n;        		Increases fixed cast time of all skills by n%
-bonus2 bFixedCastrate,sk,n;    		Increases fixed cast time of skill sk by n%
-bonus bVariableCastrate,n;     		Increases variable cast time of all skills by n%
-bonus2 bVariableCastrate,sk,n; 		Increases variable cast time of skill sk by n%
-bonus bFixedCast,t;            		Increases fixed cast time of all skills by t milliseconds
-bonus2 bSkillFixedCast,sk,t;   		Increases fixed cast time of skill sk by t milliseconds
-bonus bVariableCast,t;         		Increases variable cast time of all skills by t milliseconds
-bonus2 bSkillVariableCast,sk,t;		Increases variable cast time of skill sk by t milliseconds
+bonus bCastrate,n;             		Skill cast time rate + n%. (If RENEWAL_CAST is defined, this bonus is equal to bVariableCastrate)
+bonus2 bCastrate,sk,n;         		Adjust casting time of skill sk by n%.(If RENEWAL_CAST is defined, this bonus is equal to bVariableCastrate)
+
+bonus bFixedCastrate,n;        		Increases fixed cast time of all skills by n% (has effect in RENEWAL_CAST only)
+bonus2 bFixedCastrate,sk,n;    		Increases fixed cast time of skill sk by n% (has effect in RENEWAL_CAST only)
+bonus bVariableCastrate,n;     		Increases variable cast time of all skills by n%. (If RENEWAL_CAST is NOT defined, this bonus is equal to bCastrate)
+bonus2 bVariableCastrate,sk,n; 		Increases variable cast time of skill sk by n% (If RENEWAL_CAST is NOT defined, this bonus is equal to bCastrate)
+
+bonus bFixedCast,t;            		Increases fixed cast time of all skills by t milliseconds (has effect in RENEWAL_CAST only)
+bonus2 bSkillFixedCast,sk,t;   		Increases fixed cast time of skill sk by t milliseconds (has effect in RENEWAL_CAST only)
+bonus bVariableCast,t;         		Increases variable cast time of all skills by t milliseconds (has effect in RENEWAL_CAST only)
+bonus2 bSkillVariableCast,sk,t;		Increases variable cast time of skill sk by t milliseconds (has effect in RENEWAL_CAST only)
 
 bonus bNoCastCancel,n; 			Prevents casting from being interrupted when hit (does not work in GvG | n is meaningless)
 bonus bNoCastCancel2,n;			Prevents casting from being interrupted when hit (works even in GvG | n is meaningless)

+ 1 - 1
sql-files/item_db_re.sql

@@ -1111,7 +1111,7 @@ REPLACE INTO `item_db_re` VALUES (1823,'BF_Knuckle1','Valorous Battle Fist',5,20
 REPLACE INTO `item_db_re` VALUES (1824,'BF_Knuckle2','Brave Battle Fist',5,20,NULL,0,'30',NULL,1,0,0x00008100,63,2,2,3,'80',1,12,'bonus bStr,2; bonus bInt,1; bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-25; autobonus "{ bonus2 bVariableCastrate,\\\"MO_EXTREMITYFIST\\\",-100; }",50,6000,BF_WEAPON,"{ specialeffect2 EF_SUFFRAGIUM; }"; bonus bUnbreakableWeapon,0;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (1825,'Horn_Of_Hilthrion','Horn of Hillslion',5,20,NULL,600,'95',NULL,1,3,0x00008000,18,2,2,3,'60',1,12,'bonus3 bAutoSpell,"NPC_CRITICALWOUND",1,100; bonus4 bAutoSpellOnSkill,"CH_PALMSTRIKE","MO_INVESTIGATE",1,100; bonus3 bAutoSpell,"MO_CALLSPIRITS",5,100;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (1826,'Krieger_Knuckle1','Glorious Claw',5,20,NULL,0,'30',NULL,1,0,0x00008100,63,2,2,4,'80',1,12,'bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus3 bAutoSpell,"MO_INVESTIGATE",5,(getrefine()*10-50); bonus3 bAutoSpell,"AL_DECAGI",1,(getrefine()*10-50); }',NULL,NULL);
-REPLACE INTO `item_db_re` VALUES (1827,'Krieger_Knuckle2','Glorious Fist',5,20,NULL,0,'30',NULL,1,0,0x00008100,63,2,2,4,'80',1,12,'bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-100; bonus4 bautospellonskill,"MO_EXPLOSIONSPIRITS","CH_SOULCOLLECT",1,1000; bonus bFixedCastrate,-100; }',NULL,NULL);
+REPLACE INTO `item_db_re` VALUES (1827,'Krieger_Knuckle2','Glorious Fist',5,20,NULL,0,'30',NULL,1,0,0x00008100,63,2,2,4,'80',1,12,'bonus2 bAddRace,RC_DemiHuman,95; bonus2 bAddRace,RC_Player,95; bonus2 bIgnoreDefRaceRate,RC_DemiHuman,20; bonus2 bIgnoreDefRaceRate,RC_Player,20; bonus bUnbreakableWeapon,0; if(getrefine()>5) { bonus2 bAddRace,RC_DemiHuman,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bAddRace,RC_Player,pow(((getrefine()>14)?14:getrefine())-4,2); bonus2 bIgnoreDefRaceRate,RC_DemiHuman,5; bonus2 bIgnoreDefRaceRate,RC_Player,5; } if(getrefine()>8) { bonus2 bVariableCastrate,"MO_EXTREMITYFIST",-100; bonus2 bFixedCastrate,"MO_EXTREMITYFIST",-100; bonus4 bautospellonskill,"MO_EXPLOSIONSPIRITS","CH_SOULCOLLECT",1,1000; }',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (1828,'Monk_Knuckle','Monk Knuckle',5,20,NULL,0,'150',NULL,1,0,0x00008100,63,2,2,4,'0',0,12,'bonus bInt,2; bonus2 bSkillAtk,"MO_FINGEROFFENSIVE",25;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (1829,'Fist_C','Fist',5,0,NULL,0,'150',NULL,1,0,0x00008100,63,2,2,3,'1',0,12,'bonus2 bAddSize,Size_All,40;',NULL,NULL);
 REPLACE INTO `item_db_re` VALUES (1830,'Sura_Rampage','Sura Rampage',5,20,NULL,500,'142',NULL,1,1,0x00008100,63,2,2,3,'102',1,12,'bonus2 bSkillAtk,"SR_EARTHSHAKER",20; bonus2 bSkillAtk,"SR_SKYNETBLOW",20; bonus bUseSPrate,5; if(getrefine()>6) { bonus bUseSPrate,-1*(getrefine()-6); }',NULL,NULL);

+ 6 - 4
src/config/const.h

@@ -99,10 +99,12 @@
 
 // Renewal variable cast time reduction
 #ifdef RENEWAL_CAST
-	#define VARCAST_REDUCTION(val){ \
-		if( (varcast_r += (val)) != 0 && varcast_r >= 0 ) \
-			time = time * (1 - (float)min((val), 100) / 100); \
-	}
+	// Multiply the Variable CastTime
+	#define VARCAST_REDUCTION(val) ( time = time * (1 + (val) * 0.01) )
+
+	// Get the highest rate TO REDUCE Fixed CastTime
+	// -100 is "the highest" rate than 100.
+	#define FIXEDCASTRATE(fcast,val) ( ((fcast) == 0) ? ((fcast) = (val)) : ((fcast) = min((fcast),(val))) )
 #endif
 /**
  * End of File

+ 24 - 21
src/config/renewal.h

@@ -16,57 +16,60 @@
  * @INFO: This file holds general-purpose renewal settings, for class-specific ones check /src/config/classes folder
  **/
 
-/// game renewal server mode
+/// Game renewal server mode
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal specific support such as renewal formulas
+/// Leave this line to enable renewal specific support such as renewal formulas
 #define RENEWAL
 
-/// renewal cast time
+/// Renewal cast time
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal casting time algorithms
-/// cast time is decreased by DEX * 2 + INT while 20% of the cast time is not reduced by stats.
-/// example:
-///  on a skill whos cast time is 10s, only 8s may be reduced. the other 2s are part of a
-///  "fixed cast time" which can only be reduced by specialist items and skills
+/// Leave this line to enable renewal casting time algorithms and enable fixed cast bonuses.
+/// See also default_fixed_castrate in conf/battle/skill.conf for default fixed cast time (default is 20%).
+/// Cast time is altered be 2 portion, Variable Cast Time (VCT) and Fixed Cast Time (FCT).
+/// By default FCT is 20% of VCT (some skills aren't)
+/// - VCT is decreased by DEX * 2 + INT.
+/// - FCT is NOT reduced by stats, reduced by equips or buffs.
+/// Example:
+///  On a skill whos cast time is 10s, only 8s may be reduced. the other 2s are part of a FCT
 #define RENEWAL_CAST
 
-/// renewal drop rate algorithms
+/// Renewal drop rate algorithms
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal item drop rate algorithms
-/// while enabled a special modified based on the difference between the player and monster level is applied
-/// based on the http://irowiki.org/wiki/Drop_System#Level_Factor table
+/// Leave this line to enable renewal item drop rate algorithms
+/// While enabled a special modified based on the difference between the player and monster level is applied
+/// Based on the http://irowiki.org/wiki/Drop_System#Level_Factor table
 #define RENEWAL_DROP
 
-/// renewal exp rate algorithms
+/// Renewal exp rate algorithms
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal item exp rate algorithms
-/// while enabled a special modified based on the difference between the player and monster level is applied
+/// Leave this line to enable renewal item exp rate algorithms
+/// While enabled a special modified based on the difference between the player and monster level is applied
 #define RENEWAL_EXP
 
-/// renewal level modifier on damage
+/// Renewal level modifier on damage
 /// (disable by commenting the line)
 ///
-// leave this line to enable renewal base level modifier on skill damage (selected skills only)
+// Leave this line to enable renewal base level modifier on skill damage (selected skills only)
 #define RENEWAL_LVDMG
 
-/// renewal ASPD [malufett]
+/// Renewal ASPD [malufett]
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal ASPD
+/// Leave this line to enable renewal ASPD
 /// - shield penalty is applied
 /// - AGI has a greater factor in ASPD increase
 /// - there is a change in how skills/items give ASPD
 /// - some skill/item ASPD bonuses won't stack
 #define RENEWAL_ASPD
 
-/// renewal stat calculations
+/// Renewal stat calculations
 /// (disable by commenting the line)
 ///
-/// leave this line to enable renewal calculation for increasing status/parameter points
+/// Leave this line to enable renewal calculation for increasing status/parameter points
 #define RENEWAL_STAT
 
 #endif

+ 1 - 0
src/map/battle.c

@@ -7941,6 +7941,7 @@ static const struct _battle_data {
 	{ "boss_icewall_walk_block",            &battle_config.boss_icewall_walk_block,         0,      0,      255,            },
 	{ "snap_dodge",                         &battle_config.snap_dodge,                      0,      0,      1,              },
 	{ "stormgust_knockback",                &battle_config.stormgust_knockback,             1,      0,      1,              },
+	{ "default_fixed_castrate",             &battle_config.default_fixed_castrate,          20,     0,      100,            },
 };
 
 #ifndef STATS_OPT_OUT

+ 1 - 0
src/map/battle.h

@@ -579,6 +579,7 @@ extern struct Battle_Config
 	int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester]
 	int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
 	int stormgust_knockback;
+	int default_fixed_castrate;
 } battle_config;
 
 void do_init_battle(void);

+ 73 - 72
src/map/pc.c

@@ -2497,13 +2497,6 @@ void pc_bonus(struct map_session_data *sd,int type,int val)
 				break;
 			sd->bonus.sp += val;
 			break;
-#ifndef RENEWAL_CAST
-		case SP_VARCASTRATE:
-#endif
-		case SP_CASTRATE:
-			if(sd->state.lr_flag != 2)
-				sd->castrate+=val;
-			break;
 		case SP_MAXHPRATE:
 			if(sd->state.lr_flag != 2)
 				sd->hprate+=val;
@@ -2911,26 +2904,38 @@ void pc_bonus(struct map_session_data *sd,int type,int val)
 				sd->bonus.itemhealrate2 += val;
 			break;
 		case SP_EMATK:
-			   if(sd->state.lr_flag != 2)
-				   sd->bonus.ematk += val;
-			   break;
+			if(sd->state.lr_flag != 2)
+				sd->bonus.ematk += val;
+			break;
+#ifdef RENEWAL_CAST
 		case SP_FIXCASTRATE:
 			if(sd->state.lr_flag != 2)
-				sd->bonus.fixcastrate -= val;
+				FIXEDCASTRATE(sd->bonus.fixcastrate,val);
 			break;
 		case SP_ADD_FIXEDCAST:
 			if(sd->state.lr_flag != 2)
 				sd->bonus.add_fixcast += val;
 			break;
-#ifdef RENEWAL_CAST
+		case SP_CASTRATE:
 		case SP_VARCASTRATE:
 			if(sd->state.lr_flag != 2)
-				sd->bonus.varcastrate -= val;
+				sd->bonus.varcastrate += val;
 			break;
 		case SP_ADD_VARIABLECAST:
 			if(sd->state.lr_flag != 2)
 				sd->bonus.add_varcast += val;
 			break;
+#else
+		case SP_ADD_FIXEDCAST:
+		case SP_FIXCASTRATE:
+		case SP_ADD_VARIABLECAST:
+			//ShowWarning("pc_bonus: non-RENEWAL_CAST doesn't support this bonus %d.\n", type);
+			break;
+		case SP_VARCASTRATE:
+		case SP_CASTRATE:
+			if(sd->state.lr_flag != 2)
+				sd->castrate += val;
+			break;
 #endif
 		case SP_ADDMAXWEIGHT:
 			if (sd->state.lr_flag != 2)
@@ -3315,49 +3320,6 @@ void pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
 			sd->skillblown[i].val = val;
 		}
 		break;
-#ifndef RENEWAL_CAST
-	case SP_VARCASTRATE: // bonus2 bVariableCastrate,sk,n;
-#endif
-	case SP_CASTRATE: // bonus2 bCastrate,sk,n;
-		if(sd->state.lr_flag == 2)
-			break;
-		ARR_FIND(0, ARRAYLENGTH(sd->skillcast), i, sd->skillcast[i].id == 0 || sd->skillcast[i].id == type2);
-		if (i == ARRAYLENGTH(sd->skillcast))
-		{	//Better mention this so the array length can be updated. [Skotlex]
-			ShowError("run_script: %s: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n",
-
-#ifndef RENEWAL_CAST
-				"SP_VARCASTRATE",
-#else
-				"SP_CASTRATE",
-#endif
-
-				ARRAYLENGTH(sd->skillcast), type2, val);
-			break;
-		}
-		if(sd->skillcast[i].id == type2)
-			sd->skillcast[i].val += val;
-		else {
-			sd->skillcast[i].id = type2;
-			sd->skillcast[i].val = val;
-		}
-		break;
-	case SP_FIXCASTRATE: // bonus2 bFixedCastrate,sk,n;
-		if(sd->state.lr_flag == 2)
-			break;
-		ARR_FIND(0, ARRAYLENGTH(sd->skillfixcastrate), i, sd->skillfixcastrate[i].id == 0 || sd->skillfixcastrate[i].id == type2);
-		if (i == ARRAYLENGTH(sd->skillfixcastrate))
-		{
-			ShowError("run_script: SP_FIXCASTRATE: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n", ARRAYLENGTH(sd->skillfixcastrate), type2, val);
-			break;
-		}
-		if(sd->skillfixcastrate[i].id == type2)
-			sd->skillfixcastrate[i].val -= val;
-		else {
-			sd->skillfixcastrate[i].id = type2;
-			sd->skillfixcastrate[i].val -= val;
-		}
-		break;
 	case SP_HP_LOSS_RATE: // bonus2 bHPLossRate,n,t;
 		if(sd->state.lr_flag != 2) {
 			sd->hp_loss.value = type2;
@@ -3538,6 +3500,7 @@ void pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
 			sd->skillcooldown[i].val = val;
 		}
 		break;
+#ifdef RENEWAL_CAST
 	case SP_SKILL_FIXEDCAST: // bonus2 bSkillFixedCast,sk,t;
 		if(sd->state.lr_flag == 2)
 			break;
@@ -3570,21 +3533,61 @@ void pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
 			sd->skillvarcast[i].val = val;
 		}
 		break;
-#ifdef RENEWAL_CAST
+	case SP_CASTRATE: // bonus2 bCastrate,sk,n;
 	case SP_VARCASTRATE: // bonus2 bVariableCastrate,sk,n;
 		if(sd->state.lr_flag == 2)
 			break;
-		ARR_FIND(0, ARRAYLENGTH(sd->skillcast), i, sd->skillcast[i].id == 0 || sd->skillcast[i].id == type2);
-		if (i == ARRAYLENGTH(sd->skillcast))
+		ARR_FIND(0, ARRAYLENGTH(sd->skillcastrate), i, sd->skillcastrate[i].id == 0 || sd->skillcastrate[i].id == type2);
+		if (i == ARRAYLENGTH(sd->skillcastrate))
 		{
-			ShowError("pc_bonus2: SP_VARCASTRATE: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n",ARRAYLENGTH(sd->skillcast), type2, val);
+			ShowError("pc_bonus2: SP_VARCASTRATE: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n",ARRAYLENGTH(sd->skillcastrate), type2, val);
+			break;
+		}
+		if(sd->skillcastrate[i].id == type2)
+			sd->skillcastrate[i].val += val;
+		else {
+			sd->skillcastrate[i].id = type2;
+			sd->skillcastrate[i].val += val;
+		}
+		break;
+	case SP_FIXCASTRATE: // bonus2 bFixedCastrate,sk,n;
+		if(sd->state.lr_flag == 2)
+			break;
+		ARR_FIND(0, ARRAYLENGTH(sd->skillfixcastrate), i, sd->skillfixcastrate[i].id == 0 || sd->skillfixcastrate[i].id == type2);
+		if (i == ARRAYLENGTH(sd->skillfixcastrate))
+		{
+			ShowError("pc_bonus2: SP_FIXCASTRATE: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n", ARRAYLENGTH(sd->skillfixcastrate), type2, val);
+			break;
+		}
+		if(sd->skillfixcastrate[i].id == type2)
+			FIXEDCASTRATE(sd->skillfixcastrate[i].val,val);
+		else {
+			sd->skillfixcastrate[i].id = type2;
+			sd->skillfixcastrate[i].val = val;
+		}
+		break;
+#else
+	case SP_SKILL_FIXEDCAST: // bonus2 bSkillFixedCast,sk,t;
+	case SP_SKILL_VARIABLECAST: // bonus2 bSkillVariableCast,sk,t;
+	case SP_FIXCASTRATE: // bonus2 bFixedCastrate,sk,n;
+		//ShowWarning("pc_bonus2: Non-RENEWAL_CAST doesn't support this bonus %d.\n", type);
+		break;
+	case SP_VARCASTRATE: // bonus2 bVariableCastrate,sk,n;
+	case SP_CASTRATE: // bonus2 bCastrate,sk,n;
+		if(sd->state.lr_flag == 2)
+			break;
+		ARR_FIND(0, ARRAYLENGTH(sd->skillcastrate), i, sd->skillcastrate[i].id == 0 || sd->skillcastrate[i].id == type2);
+		if (i == ARRAYLENGTH(sd->skillcastrate))
+		{	//Better mention this so the array length can be updated. [Skotlex]
+			ShowError("pc_bonus2: %s: Reached max (%d) number of skills per character, bonus skill %d (+%d%%) lost.\n",
+				(type == SP_CASTRATE) ? "SP_CASTRATE" : "SP_VARCASTRATE", ARRAYLENGTH(sd->skillcastrate), type2, val);
 			break;
 		}
-		if(sd->skillcast[i].id == type2)
-			sd->skillcast[i].val -= val;
+		if(sd->skillcastrate[i].id == type2)
+			sd->skillcastrate[i].val += val;
 		else {
-			sd->skillcast[i].id = type2;
-			sd->skillcast[i].val -= val;
+			sd->skillcastrate[i].id = type2;
+			sd->skillcastrate[i].val = val;
 		}
 		break;
 #endif
@@ -7572,12 +7575,6 @@ int pc_readparam(struct map_session_data* sd,int type)
 		case SP_FLEE1:		     val = sd->battle_status.flee; break;
 		case SP_FLEE2:		     val = sd->battle_status.flee2; break;
 		case SP_DEFELE:		     val = sd->battle_status.def_ele; break;
-#ifndef RENEWAL_CAST
-		case SP_VARCASTRATE:
-#endif
-		case SP_CASTRATE:
-				val = sd->castrate+=val;
-			break;
 		case SP_MAXHPRATE:	     val = sd->hprate; break;
 		case SP_MAXSPRATE:	     val = sd->sprate; break;
 		case SP_SPRATE:		     val = sd->dsprate; break;
@@ -7658,9 +7655,13 @@ int pc_readparam(struct map_session_data* sd,int type)
 		case SP_EMATK:           val = sd->bonus.ematk; break;
 		case SP_FIXCASTRATE:     val = sd->bonus.fixcastrate; break;
 		case SP_ADD_FIXEDCAST:   val = sd->bonus.add_fixcast; break;
+		case SP_ADD_VARIABLECAST:  val = sd->bonus.add_varcast; break;
+		case SP_CASTRATE:
+		case SP_VARCASTRATE:
 #ifdef RENEWAL_CAST
-		case SP_VARCASTRATE:     val = sd->bonus.varcastrate; break;
-		case SP_ADD_VARIABLECAST:val = sd->bonus.add_varcast; break;
+			val = sd->bonus.varcastrate; break;
+#else
+			val = sd->castrate; break;
 #endif
 	}
 

+ 3 - 3
src/map/pc.h

@@ -355,7 +355,7 @@ struct map_session_data {
 	struct s_skill_bonus { //skillatk raises bonus dmg% of skills, skillheal increases heal%, skillblown increases bonus blewcount for some skills.
 		unsigned short id;
 		short val;
-	} skillatk[MAX_PC_BONUS], skillusesprate[MAX_PC_BONUS], skillusesp[MAX_PC_BONUS], skillheal[MAX_PC_BONUS], skillheal2[MAX_PC_BONUS], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS], skillcooldown[MAX_PC_BONUS], skillfixcast[MAX_PC_BONUS], skillvarcast[MAX_PC_BONUS], skillfixcastrate[MAX_PC_BONUS];
+	} skillatk[MAX_PC_BONUS], skillusesprate[MAX_PC_BONUS], skillusesp[MAX_PC_BONUS], skillheal[MAX_PC_BONUS], skillheal2[MAX_PC_BONUS], skillblown[MAX_PC_BONUS], skillcastrate[MAX_PC_BONUS], skillcooldown[MAX_PC_BONUS], skillfixcast[MAX_PC_BONUS], skillvarcast[MAX_PC_BONUS], skillfixcastrate[MAX_PC_BONUS];
 	struct s_regen {
 		short value;
 		int rate;
@@ -419,8 +419,8 @@ struct map_session_data {
 		unsigned short unbreakable;	// chance to prevent ANY equipment breaking [celest]
 		unsigned short unbreakable_equip; //100% break resistance on certain equipment
 		unsigned short unstripable_equip;
-		int fixcastrate,varcastrate;
-		int add_fixcast,add_varcast;
+		int fixcastrate, varcastrate; // n/100
+		int add_fixcast, add_varcast; // in milliseconds
 		int ematk; // matk bonus from equipment
 		int eatk; // atk bonus from equipment
 	} bonus;

+ 95 - 63
src/map/skill.c

@@ -15420,11 +15420,11 @@ int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) {
 			int i;
 			if( sd->castrate != 100 )
 				time = time * sd->castrate / 100;
-			for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
+			for( i = 0; i < ARRAYLENGTH(sd->skillcastrate) && sd->skillcastrate[i].id; i++ )
 			{
-				if( sd->skillcast[i].id == skill_id )
+				if( sd->skillcastrate[i].id == skill_id )
 				{
-					time+= time * sd->skillcast[i].val / 100;
+					time += time * sd->skillcastrate[i].val / 100;
 					break;
 				}
 			}
@@ -15459,8 +15459,8 @@ int skill_castfix_sc(struct block_list *bl, int time)
 	if (sc && sc->count) {
 		if (sc->data[SC_SLOWCAST])
 			time += time * sc->data[SC_SLOWCAST]->val2 / 100;
-	if (sc->data[SC_PARALYSIS])
-		time += sc->data[SC_PARALYSIS]->val3;
+		if (sc->data[SC_PARALYSIS])
+			time += sc->data[SC_PARALYSIS]->val3;
 		if (sc->data[SC_SUFFRAGIUM]) {
 			time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100;
 			status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
@@ -15482,7 +15482,14 @@ int skill_castfix_sc(struct block_list *bl, int time)
 }
 #else
 /**
- * Get the skill cast time for RENEWAL_CAST
+ * Get the skill cast time for RENEWAL_CAST.
+ * FixedRate reduction never be stacked, always get the HIGHEST VALUE TO REDUCE (-20% vs 10%, -20% wins!)
+ * Additive value:
+ *    Variable CastTime : time  += value
+ *    Fixed CastTime    : fixed += value
+ * Multipicative value
+ *    Variable CastTime : VARCAST_REDUCTION(value)
+ *    Fixed CastTime    : FIXEDCASTRATE2(value)
  * @param bl: The caster
  * @param time: Cast time without reduction
  * @param skill_id: Skill ID of the casted skill
@@ -15493,7 +15500,13 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16
 {
 	struct status_change *sc = status_get_sc(bl);
 	struct map_session_data *sd = BL_CAST(BL_PC,bl);
-	int fixed = skill_get_fixed_cast(skill_id, skill_lv), fixcast_r = 0, varcast_r = 0, i = 0;
+	int fixed = skill_get_fixed_cast(skill_id, skill_lv);
+	short fixcast_r = 0;
+	uint8 i = 0, flag = skill_get_castnodex(skill_id, skill_lv);
+
+#define FIXEDCASTRATE2(val) ( FIXEDCASTRATE(fixcast_r,(val)) )
+
+	nullpo_ret(bl);
 
 	if( time < 0 )
 		return 0;
@@ -15501,97 +15514,116 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16
 	if( bl->type == BL_MOB )
 		return (int)time;
 
-	if( fixed == 0 ){
-		fixed = (int)time * 20 / 100; // fixed time
-		time = time * 80 / 100; // variable time
-	}else if( fixed < 0 ) // no fixed cast time
+	if( fixed < 0 || battle_config.default_fixed_castrate == 0 ) // no fixed cast time
 		fixed = 0;
+	else if( fixed == 0 ) {
+		fixed = (int)time * battle_config.default_fixed_castrate / 100; // fixed time
+		time = time * (100 - battle_config.default_fixed_castrate) / 100; // variable time
+	}
+
+	// Additive Variable Cast bonus first
+	if (sd && !(flag&4)) { // item bonus
+		time += sd->bonus.add_varcast; // bonus bVariableCast
+
+		for (i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++)
+			if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast
+				time += sd->skillvarcast[i].val;
+				break;
+			}
+	}
+	/*if (sc && sc->count && !(flag&2)) { // status change
+		// -NONE YET-
+		//	if (sc->data[????])
+		//		bonus += sc->data[????]->val?;
+	}*/
+
+	// Adjusted by item bonuses
+	if (sd && !(flag&4)) {
+		// Additive values
+		fixed += sd->bonus.add_fixcast; // bonus bFixedCast
 
-	if(sd  && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ // Increases/Decreases fixed/variable cast time of a skill by item/card bonuses.
-		if( sd->bonus.varcastrate < 0 )
-			VARCAST_REDUCTION(sd->bonus.varcastrate);
-		if( sd->bonus.add_varcast != 0 ) // bonus bVariableCast
-			time += sd->bonus.add_varcast;
-		if( sd->bonus.add_fixcast != 0 ) // bonus bFixedCast
-			fixed += sd->bonus.add_fixcast;
 		for (i = 0; i < ARRAYLENGTH(sd->skillfixcast) && sd->skillfixcast[i].id; i++)
 			if (sd->skillfixcast[i].id == skill_id){ // bonus2 bSkillFixedCast
 				fixed += sd->skillfixcast[i].val;
 				break;
 			}
-		for( i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++ )
-			if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast
-				time += sd->skillvarcast[i].val;
-				break;
-			}
-		for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
-			if( sd->skillcast[i].id == skill_id ){ // bonus2 bVariableCastrate
-				VARCAST_REDUCTION(sd->skillcast[i].val);
+
+		// Multipicative values
+		if (sd->bonus.varcastrate != 0)
+			VARCAST_REDUCTION(sd->bonus.varcastrate); // bonus bVariableCastrate
+
+		if (sd->bonus.fixcastrate != 0)
+			FIXEDCASTRATE2(sd->bonus.fixcastrate); // bonus bFixedCastrate
+
+		for( i = 0; i < ARRAYLENGTH(sd->skillcastrate) && sd->skillcastrate[i].id; i++ )
+			if( sd->skillcastrate[i].id == skill_id ){ // bonus2 bVariableCastrate
+				VARCAST_REDUCTION(sd->skillcastrate[i].val);
 				break;
 			}
 		for( i = 0; i < ARRAYLENGTH(sd->skillfixcastrate) && sd->skillfixcastrate[i].id; i++ )
 			if( sd->skillfixcastrate[i].id == skill_id ){ // bonus2 bFixedCastrate
-				fixcast_r = sd->skillfixcastrate[i].val;
+				FIXEDCASTRATE2(sd->skillfixcastrate[i].val);
 				break;
 			}
 	}
 
-	if (sc && sc->count && !(skill_get_castnodex(skill_id, skill_lv)&2) ) {
-		// All variable cast additive bonuses must come first
+	// Adjusted by active statuses
+	if (sc && sc->count && !(flag&2) ) {
+		// Multiplicative Variable CastTime values
 		if (sc->data[SC_SLOWCAST])
-			VARCAST_REDUCTION(-sc->data[SC_SLOWCAST]->val2);
-		if( sc->data[SC__LAZINESS] )
-			VARCAST_REDUCTION(-sc->data[SC__LAZINESS]->val2);
+			VARCAST_REDUCTION(sc->data[SC_SLOWCAST]->val2);
+		if (sc->data[SC__LAZINESS])
+			VARCAST_REDUCTION(sc->data[SC__LAZINESS]->val2);
 
-		// Variable cast reduction bonuses
 		if (sc->data[SC_SUFFRAGIUM]) {
-			VARCAST_REDUCTION(sc->data[SC_SUFFRAGIUM]->val2);
+			VARCAST_REDUCTION(-sc->data[SC_SUFFRAGIUM]->val2);
 			status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
 		}
 		if (sc->data[SC_MEMORIZE]) {
-			VARCAST_REDUCTION(50);
+			VARCAST_REDUCTION(-50);
 			if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
 				status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
 		}
 		if (sc->data[SC_POEMBRAGI])
-			VARCAST_REDUCTION(sc->data[SC_POEMBRAGI]->val2);
+			VARCAST_REDUCTION(-sc->data[SC_POEMBRAGI]->val2);
 		if (sc->data[SC_IZAYOI])
-			VARCAST_REDUCTION(50);
+			VARCAST_REDUCTION(-50);
 		if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WATER))
-			VARCAST_REDUCTION(30); //Reduces 30% Variable Cast Time of Water spells.
-		if( sc->data[SC_TELEKINESIS_INTENSE] )
-			VARCAST_REDUCTION(sc->data[SC_TELEKINESIS_INTENSE]->val2);
-		// Fixed cast reduction bonuses
-		if( sc->data[SC_SECRAMENT] )
-			fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2);
-		if( sd && ( skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP  )
-			fixcast_r = max(fixcast_r, 5 + skill_lv * 5);
-		// Fixed cast non percentage bonuses
-		if( sc->data[SC_MANDRAGORA] )
+			VARCAST_REDUCTION(-30); //Reduces 30% Variable Cast Time of Water spells.
+		if (sc->data[SC_TELEKINESIS_INTENSE])
+			VARCAST_REDUCTION(-sc->data[SC_TELEKINESIS_INTENSE]->val2);
+
+		// Multiplicative Fixed CastTime values
+		if (sc->data[SC_SECRAMENT])
+			FIXEDCASTRATE2(-sc->data[SC_SECRAMENT]->val2);
+		if (sd && (skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP)
+			FIXEDCASTRATE2(-(5 + skill_lv * 5));
+		if (sc->data[SC_DANCEWITHWUG])
+			FIXEDCASTRATE2(-sc->data[SC_DANCEWITHWUG]->val4);
+		if (sc->data[SC_HEAT_BARREL])
+			FIXEDCASTRATE2(-sc->data[SC_HEAT_BARREL]->val2);
+
+		// Additive Fixed CastTime values
+		if (sc->data[SC_MANDRAGORA])
 			fixed += sc->data[SC_MANDRAGORA]->val1 * 1000 / 2;
-		if( sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION] )
+
+		if (sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION])
 			fixed -= 1000;
-		if (sc->data[SC_DANCEWITHWUG])
-			fixed -= fixed * sc->data[SC_DANCEWITHWUG]->val4 / 100;
-		if( sc->data[SC_HEAT_BARREL] )
-			fixcast_r = max(fixcast_r, sc->data[SC_HEAT_BARREL]->val2);
 		if (sc->data[SC_IZAYOI])
 			fixed = 0;
 	}
 
-	if( sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){
-		VARCAST_REDUCTION( max(sd->bonus.varcastrate, 0) + max(i, 0) );
-		fixcast_r = max(fixcast_r, sd->bonus.fixcastrate) + min(sd->bonus.fixcastrate,0);
-	}
+	// Apply Variable CastTime calculation by INT & DEX
+	if (!(flag&1))
+		time = time * (1 - sqrt(((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale)));
+
+	// Apply Fixed CastTime rate
+	if (fixed != 0 && fixcast_r != 0)
+		fixed = (int)(fixed * (1 + fixcast_r * 0.01));
 
-	if( varcast_r < 0 ) // now compute overall factors
-		time = time * (1 - (float)varcast_r / 100);
-	if( !(skill_get_castnodex(skill_id, skill_lv)&1) )// reduction from status point
-		time = (1 - sqrt( ((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale) )) * time;
-	// underflow checking/capping
-	time = max(time, 0) + (1 - (float)min(fixcast_r, 100) / 100) * max(fixed,0);
+#undef FIXEDCASTRATE2
 
-	return (int)time;
+	return (int)max(time + fixed, 0);
 }
 #endif
 

+ 1 - 1
src/map/status.c

@@ -2935,7 +2935,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 		+ sizeof(sd->skillheal)
 		+ sizeof(sd->skillheal2)
 		+ sizeof(sd->skillblown)
-		+ sizeof(sd->skillcast)
+		+ sizeof(sd->skillcastrate)
 		+ sizeof(sd->skillcooldown)
 		+ sizeof(sd->skillfixcast)
 		+ sizeof(sd->skillvarcast)