Browse Source

Star Crumbs, Spirit Spheres, EDP, Guillotine Fist, Sand Attack, Rapid Smiting, Clashing Spiral (#8219)

- ATKpercent, Spirit Spheres and other mastery bonuses, now all affect the exactly same skills with some exceptions for refine and Star Crumbs, see battle_skill_stacks_masteries_vvs
- EDP and elemental bonus damage now apply to the exact same list of skills, see battle_skill_stacks_edp_element (pre-re)
- Star Crumbs and Spirit Spheres no longer multiply the damage by number of hits twice
- Star Crumbs, Spirit Spheres and similar mastery bonuses that apply even on MISS are now applied after the attribute table but before EDP and elemental bonus damage (pre-re)
- Attribute table can now make damage go negative which is weighted against any of the above mastery bonuses and elemental bonus damage before being capped to 0
- EDP and elemental bonus damage no longer apply to the left hand; removed "basedamage2" as it is no longer needed
- Left-hand damage is now always capped to 0 instead of 1
- Fixed Guillotine Fist ignoring size modifiers
- Fixed inaccuracies in Guillotine Fist's damage due to rounding issues
- Fixed bonus damage from Magnum Break when using Guillotine Fist
- Sand Attack is no longer influenced by EDP
- Rapid Smiting now has a fixed 20% hit bonus (it hits at least to 25%)
- Rapid Smiting no longer deals damage on MISS
- Clashing Spiral damage is no longer influenced by ATKpercent and any mastery except Star Crumbs
- Further code improvements / compiler warning fix
- Fixes #8211 
- Fixes #8218 
- Fixes #7750 
- Fixes #1823
Playtester 1 year ago
parent
commit
537072e610
3 changed files with 252 additions and 193 deletions
  1. 250 188
      src/map/battle.cpp
  2. 2 3
      src/map/battle.hpp
  3. 0 2
      src/map/skill.cpp

+ 250 - 188
src/map/battle.cpp

@@ -2449,11 +2449,11 @@ static int battle_calc_base_weapon_attack(struct block_list *src, struct status_
  * This applies to pre-renewal and non-sd in renewal
  *------------------------------------------
  * Pass damage2 as NULL to not calc it.
- * Flag values:
+ * Flag values (see e_base_damage_flag):
  * &1 : Critical hit
  * &2 : Arrow attack
  * &4 : Skill is Magic Crasher
- * &8 : Skip target size adjustment (Extremity Fist?)
+ * &8 : Skip target size adjustment (Weapon Perfection)
  * &16: Arrow attack but BOW, REVOLVER, RIFLE, SHOTGUN, GATLING or GRENADE type weapon not equipped (i.e. shuriken, kunai and venom knives not affected by DEX)
  *
  * Credits:
@@ -3180,7 +3180,6 @@ static bool is_attack_hitting(struct Damage* wd, struct block_list *src, struct
 				hitrate += hitrate * 10 * skill_lv / 100;
 				break;
 			case KN_AUTOCOUNTER:
-			case PA_SHIELDCHAIN:
 			case NPC_WATERATTACK:
 			case NPC_GROUNDATTACK:
 			case NPC_FIREATTACK:
@@ -3284,6 +3283,10 @@ static bool is_attack_hitting(struct Damage* wd, struct block_list *src, struct
 	}
 
 	hitrate = cap_value(hitrate, battle_config.min_hitrate, battle_config.max_hitrate);
+
+	if(skill_id == PA_SHIELDCHAIN)
+		hitrate += 20; //Rapid Smiting gives a flat +20 hit after the hitrate was capped
+
 	return (rnd()%100 < hitrate);
 }
 
@@ -3353,25 +3356,67 @@ static bool attack_ignores_def(struct Damage* wd, struct block_list *src, struct
 	return nk[NK_IGNOREDEFENSE] != 0;
 }
 
-/*================================================
- * Should skill attack consider VVS and masteries?
- *------------------------------------------------
- * Credits:
- *	Original coder Skotlex
- *	Initial refactoring by Baalberith
- *	Refined and optimized by helvetica
+/**
+ * This function lists which skills are unaffected by refine bonus, masteries, Star Crumbs and Spirit Spheres
+ * This function is also used to determine if atkpercent applies
+ * @param skill_id: Skill being used
+ * @param type 1 - Checking refine bonus; 2 - Checking Star Crumb bonus
+ * @return true = bonus applies; false = bonus does not apply
  */
-static bool battle_skill_stacks_masteries_vvs(uint16 skill_id)
+static bool battle_skill_stacks_masteries_vvs(uint16 skill_id, int type)
 {
-	if (
-#ifndef RENEWAL
-		skill_id == PA_SHIELDCHAIN || skill_id == CR_SHIELDBOOMERANG ||
-		skill_id == PA_SACRIFICE || skill_id == AM_ACIDTERROR ||
-#endif
-		skill_id == MO_INVESTIGATE || skill_id == MO_EXTREMITYFIST ||
-		skill_id == RK_DRAGONBREATH || skill_id == RK_DRAGONBREATH_WATER || skill_id == NC_SELFDESTRUCTION ||
-		skill_id == LG_SHIELDPRESS || skill_id == LG_EARTHDRIVE)
+	switch (skill_id) {
+		case PA_SHIELDCHAIN:
+		case CR_SHIELDBOOMERANG:
+		case AM_ACIDTERROR:
+		case MO_INVESTIGATE:
+		case MO_EXTREMITYFIST:
+		case PA_SACRIFICE:
+		case NPC_DRAGONBREATH:
+		case RK_DRAGONBREATH:
+		case RK_DRAGONBREATH_WATER:
+		case NC_SELFDESTRUCTION:
+		case LG_SHIELDPRESS:
+		case LG_EARTHDRIVE:
 			return false;
+		case CR_GRANDCROSS:
+		case NPC_GRANDDARKNESS:
+			// Grand Cross is influenced by refine bonus but not by atkpercent / masteries / Star Crumbs / Spirit Spheres
+			if (type != 1)
+				return false;
+			break;
+		case LK_SPIRALPIERCE:
+			// Spiral Pierce is influenced only by refine bonus and Star Crumbs
+			if (type != 1 && type != 2)
+				return false;
+			break;
+	}
+
+	return true;
+}
+
+/**
+ * This function lists which skills are unaffected by EDP and the elemental bonus from Magnum Break / EDP
+ * Unit skills (e.g. Bomb and Freezing Trap) are never affected.
+ * @param skill_id: Skill being used
+ * @return true = bonus applies; false = bonus does not apply
+ */
+static bool battle_skill_stacks_edp_element(uint16 skill_id)
+{
+	switch (skill_id) {
+		case TF_SPRINKLESAND:
+		case AS_SPLASHER:
+		case ASC_METEORASSAULT:
+		case ASC_BREAKER:
+		case AS_VENOMKNIFE:
+		case AM_ACIDTERROR:
+			return false;
+		default:
+			//Unit skills
+			if (skill_get_unit_id(skill_id))
+				return false;
+			break;
+	}
 
 	return true;
 }
@@ -3592,6 +3637,85 @@ int battle_get_misc_element(struct block_list* src, struct block_list* target, u
 	return element;
 }
 
+#define ATK_RATE(damage, damage2, a) do { int64 rate_ = (a); (damage) = (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) = (damage2) * rate_ / 100; } while(0);
+#define ATK_RATE2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) = (damage) *rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) = (damage2) * rate2_ / 100; } while(0);
+#define ATK_RATER(damage, a) { (damage) = (damage) * (a) / 100; }
+#define ATK_RATEL(damage2, a) { (damage2) = (damage2) * (a) / 100; }
+//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
+#define ATK_ADDRATE(damage, damage2, a) do { int64 rate_ = (a); (damage) += (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) += (damage2) *rate_ / 100; } while(0);
+#define ATK_ADDRATE2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) += (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) += (damage2) * rate2_ / 100; } while(0);
+//Adds an absolute value to damage. 100 = +100 damage
+#define ATK_ADD(damage, damage2, a) do { int64 rate_ = (a); (damage) += rate_; if(is_attack_left_handed(src, skill_id)) (damage2) += rate_; } while(0);
+#define ATK_ADD2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) += rate_; if(is_attack_left_handed(src, skill_id)) (damage2) += rate2_; } while(0);
+
+#ifdef RENEWAL
+	#define RE_ALLATK_ADD(wd, a) do { int64 a_ = (a); ATK_ADD((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_ADD((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_ADD((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_ADD((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
+	#define RE_ALLATK_RATE(wd, a) do { int64 a_ = (a); ATK_RATE((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_RATE((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_RATE((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_RATE((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
+	#define RE_ALLATK_ADDRATE(wd, a) do { int64 a_ = (a); ATK_ADDRATE((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_ADDRATE((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_ADDRATE((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_ADDRATE((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
+#else
+	#define RE_ALLATK_ADD(wd, a) {;}
+	#define RE_ALLATK_RATE(wd, a) {;}
+	#define RE_ALLATK_ADDRATE(wd, a) {;}
+#endif
+
+/**
+ * Cap both damage and basedamage of damage struct to a minimum value
+ * @param wd: Weapon damage structure
+ * @param src: Source of the attack
+ * @param skill_id: Skill ID of the skill used by source
+ * @param min: Minimum value to which damage should be capped
+ */
+static void battle_min_damage(struct Damage &wd, struct block_list &src, uint16 skill_id, int64 min) {
+	if (is_attack_right_handed(&src, skill_id)) {
+		wd.damage = cap_value(wd.damage, min, INT64_MAX);
+		wd.basedamage = cap_value(wd.basedamage, min, INT64_MAX);
+	}
+	// Left-hand damage is always capped to 0
+	if (is_attack_left_handed(&src, skill_id)) {
+		wd.damage2 = cap_value(wd.damage2, 0, INT64_MAX);
+	}
+}
+
+/**
+ * Returns the bonus damage granted by Spirit Spheres
+ * As we delete the spheres before calculating the damage, we need some kind of logic to figure out how many were available
+ * Each skill handles this in its own way, this function handles the different cases
+ * @param wd: Weapon damage structure
+ * @param src: Source of the attack
+ * @param skill_id: Skill ID of the skill used by source
+ * @param min: Minimum value to which damage should be capped
+ */
+static int battle_get_spiritball_damage(struct Damage& wd, struct block_list& src, uint16 skill_id) {
+
+	map_session_data* sd = BL_CAST(BL_PC, &src);
+
+	// Return 0 for non-players
+	if (!sd)
+		return 0;
+
+	int damage = 0;
+
+	switch (skill_id) {
+		case MO_INVESTIGATE:
+#ifndef RENEWAL
+		case MO_FINGEROFFENSIVE:
+#endif
+			// These skills used as many spheres as they do hits
+			damage = (wd.div_ + sd->spiritball) * 3;
+			break;
+		case MO_EXTREMITYFIST:
+			// These skills store the number of spheres the player had before cast
+			damage = sd->spiritball_old * 3;
+			break;
+		default:
+			// Any skills that do not consume spheres or do not care
+			damage = sd->spiritball * 3;
+			break;
+	}
+
+	return damage;
+}
+
 /*========================================
  * Do element damage modifier calculation
  *----------------------------------------
@@ -3603,13 +3727,13 @@ int battle_get_misc_element(struct block_list* src, struct block_list* target, u
 static void battle_calc_element_damage(struct Damage* wd, struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv)
 {
 	std::bitset<NK_MAX> nk = battle_skill_get_damage_properties(skill_id, wd->miscflag);
+	map_session_data* sd = BL_CAST(BL_PC, src);
+	status_change* sc = status_get_sc(src);
+	struct status_data* sstatus = status_get_status_data(src);
+	struct status_data* tstatus = status_get_status_data(target);
 
 	// Elemental attribute fix
 	if(!nk[NK_IGNOREELEMENT] && (wd->damage > 0 || wd->damage2 > 0)) {
-		map_session_data *sd = BL_CAST(BL_PC, src);
-		status_change *sc = status_get_sc(src);
-		struct status_data *sstatus = status_get_status_data(src);
-		struct status_data *tstatus = status_get_status_data(target);
 		int left_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_L, true);
 		int right_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_R, true);
 
@@ -3619,9 +3743,9 @@ static void battle_calc_element_damage(struct Damage* wd, struct block_list *src
 			case RK_DRAGONBREATH_WATER:
 			case NC_SELFDESTRUCTION:
 			case HFLI_SBR44:
-				wd->damage = battle_attr_fix(src, target, wd->damage, right_element, tstatus->def_ele, tstatus->ele_lv);
+				wd->damage = battle_attr_fix(src, target, wd->damage, right_element, tstatus->def_ele, tstatus->ele_lv, 1);
 				if (is_attack_left_handed(src, skill_id))
-					wd->damage2 = battle_attr_fix(src, target, wd->damage2, left_element, tstatus->def_ele, tstatus->ele_lv);
+					wd->damage2 = battle_attr_fix(src, target, wd->damage2, left_element, tstatus->def_ele, tstatus->ele_lv, 1);
 				break;
 			default:
 				if (skill_id == 0 && (battle_config.attack_attr_none & src->type))
@@ -3629,9 +3753,9 @@ static void battle_calc_element_damage(struct Damage* wd, struct block_list *src
 #ifdef RENEWAL
 				if (sd == nullptr) { // Renewal player's elemental damage calculation is already done before this point, only calculate for everything else
 #endif
-					wd->damage = battle_attr_fix(src, target, wd->damage, right_element, tstatus->def_ele, tstatus->ele_lv);
+					wd->damage = battle_attr_fix(src, target, wd->damage, right_element, tstatus->def_ele, tstatus->ele_lv, 1);
 					if (is_attack_left_handed(src, skill_id))
-						wd->damage2 = battle_attr_fix(src, target, wd->damage2, left_element, tstatus->def_ele, tstatus->ele_lv);
+						wd->damage2 = battle_attr_fix(src, target, wd->damage2, left_element, tstatus->def_ele, tstatus->ele_lv, 1);
 #ifdef RENEWAL
 				}
 #endif
@@ -3652,63 +3776,59 @@ static void battle_calc_element_damage(struct Damage* wd, struct block_list *src
 			case SR_CRESCENTELBOW_AUTOSPELL:
 			case SR_GATEOFHELL:
 			case GN_FIRE_EXPANSION_ACID:
-				wd->damage = battle_attr_fix(src, target, wd->damage, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv);
+				wd->damage = battle_attr_fix(src, target, wd->damage, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv, 1);
 				if (is_attack_left_handed(src, skill_id))
-					wd->damage2 = battle_attr_fix(src, target, wd->damage2, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv);
+					wd->damage2 = battle_attr_fix(src, target, wd->damage2, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv, 1);
 				break;
 		}
+	}
 
 #ifndef RENEWAL
-		if (sd && (wd->damage > 0 || wd->damage2 > 0)) { // Applies only to player damage, monsters and mercenaries don't get this damage boost
-			// This adds a percentual damage bonus based on the damage you would deal with a normal attack
-			// Does not apply to unit skills or skills that have their own base damage formula such as AM_ACIDTERROR
-			if (sc && sc->getSCE(SC_WATK_ELEMENT) && !skill_get_unit_id(skill_id) && skill_id != AM_ACIDTERROR) {
-				int64 damage = wd->basedamage * sc->getSCE(SC_WATK_ELEMENT)->val2;
-				damage = battle_attr_fix(src, target, damage, sc->getSCE(SC_WATK_ELEMENT)->val1, tstatus->def_ele, tstatus->ele_lv, 1);
-				//Spirit Sphere bonus damage is not affected by element
-				if (skill_id == MO_FINGEROFFENSIVE || skill_id == MO_INVESTIGATE) { //Need to calculate number of Spirit Balls you had before cast
-					damage += ((wd->div_ + sd->spiritball) * 3 * sc->getSCE(SC_WATK_ELEMENT)->val2);
-				}
-				else
-					damage += (sd->spiritball * 3 * sc->getSCE(SC_WATK_ELEMENT)->val2);
-				//Division needs to happen at the end to prevent data loss due to rounding
-				wd->damage += (damage / 100);
-				if (is_attack_left_handed(src, skill_id)) {
-					damage = wd->basedamage2 * sc->getSCE(SC_WATK_ELEMENT)->val2;
-					damage = battle_attr_fix(src, target, damage, sc->getSCE(SC_WATK_ELEMENT)->val1, tstatus->def_ele, tstatus->ele_lv, 1);
-					if (skill_id == MO_FINGEROFFENSIVE) {
-						damage += ((wd->div_ + sd->spiritball) * 3 * sc->getSCE(SC_WATK_ELEMENT)->val2);
-					}
-					else
-						damage += (sd->spiritball * 3 * sc->getSCE(SC_WATK_ELEMENT)->val2);
-					wd->damage2 += (damage / 100);
-				}
-			}
-		}
-#endif
+	// These mastery bonuses are non-elemental and should apply even if the attack misses
+	// They are still increased by the EDP/Magnum Break bonus damage (WATK_ELEMENT)
+	// In renewal these bonuses do not apply when the attack misses
+	if (sd && battle_skill_stacks_masteries_vvs(skill_id, 2)) {
+		// Star Crumb bonus damage
+		ATK_ADD2(wd->damage, wd->damage2, sd->right_weapon.star, sd->left_weapon.star);
+	}
+	// Check if general mastery bonuses apply (above check is only for Star Crumb)
+	if (battle_skill_stacks_masteries_vvs(skill_id, 0)) {
+		// Spirit Sphere bonus damage
+		ATK_ADD(wd->damage, wd->damage2, battle_get_spiritball_damage(*wd, *src, skill_id));
+
+		// Skill-specific bonuses
+		if (skill_id == TF_POISON)
+			ATK_ADD(wd->damage, wd->damage2, 15 * skill_lv);
 	}
-}
 
-#define ATK_RATE(damage, damage2, a) do { int64 rate_ = (a); (damage) = (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) = (damage2) * rate_ / 100; } while(0);
-#define ATK_RATE2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) = (damage) *rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) = (damage2) * rate2_ / 100; } while(0);
-#define ATK_RATER(damage, a) { (damage) = (damage) * (a) / 100; }
-#define ATK_RATEL(damage2, a) { (damage2) = (damage2) * (a) / 100; }
-//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
-#define ATK_ADDRATE(damage, damage2, a) do { int64 rate_ = (a); (damage) += (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) += (damage2) *rate_ / 100; } while(0);
-#define ATK_ADDRATE2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) += (damage) * rate_ / 100; if(is_attack_left_handed(src, skill_id)) (damage2) += (damage2) * rate2_ / 100; } while(0);
-//Adds an absolute value to damage. 100 = +100 damage
-#define ATK_ADD(damage, damage2, a) do { int64 rate_ = (a); (damage) += rate_; if(is_attack_left_handed(src, skill_id)) (damage2) += rate_; } while(0);
-#define ATK_ADD2(damage, damage2, a , b) do { int64 rate_ = (a), rate2_ = (b); (damage) += rate_; if(is_attack_left_handed(src, skill_id)) (damage2) += rate2_; } while(0);
+	// These bonuses do not apply to skills that ignore element, unit skills and skills that have their own base damage formula
+	// If damage was reduced below 0 and was not increased again to a positive value through mastery bonuses, these bonuses are ignored
+	// Any of these are only applied to your right hand weapon in pre-renewal
+	if (!nk[NK_IGNOREELEMENT] && (wd->damage > 0 || wd->damage2 > 0) && sc && battle_skill_stacks_edp_element(skill_id)) {
 
-#ifdef RENEWAL
-	#define RE_ALLATK_ADD(wd, a) do { int64 a_ = (a); ATK_ADD((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_ADD((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_ADD((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_ADD((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
-	#define RE_ALLATK_RATE(wd, a) do { int64 a_ = (a); ATK_RATE((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_RATE((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_RATE((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_RATE((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
-	#define RE_ALLATK_ADDRATE(wd, a) do { int64 a_ = (a); ATK_ADDRATE((wd)->statusAtk, (wd)->statusAtk2, a_); ATK_ADDRATE((wd)->weaponAtk, (wd)->weaponAtk2, a_); ATK_ADDRATE((wd)->equipAtk, (wd)->equipAtk2, a_); ATK_ADDRATE((wd)->masteryAtk, (wd)->masteryAtk2, a_); } while(0);
-#else
-	#define RE_ALLATK_ADD(wd, a) {;}
-	#define RE_ALLATK_RATE(wd, a) {;}
-	#define RE_ALLATK_ADDRATE(wd, a) {;}
+		// EDP bonus damage
+		// This has to be applied after mastery bonuses but still before the elemental extra damage
+		if (sc->getSCE(SC_EDP))
+			wd->damage += (wd->damage * sc->getSCE(SC_EDP)->val3) / 100;
+
+		// This adds a percentual damage bonus based on the damage you would deal with a normal attack
+		// Applies only to player damage; monsters and mercenaries don't get this damage boost
+		if (sd && sc->getSCE(SC_WATK_ELEMENT)) {
+			// Pretend the normal attack was of the element stored in the status change
+			wd->basedamage = battle_attr_fix(src, target, wd->basedamage, sc->getSCE(SC_WATK_ELEMENT)->val1, tstatus->def_ele, tstatus->ele_lv, 1);
+			// Star Crumb bonus damage
+			wd->basedamage += sd->right_weapon.star;
+			// Spirit Sphere bonus damage
+			wd->basedamage += battle_get_spiritball_damage(*wd, *src, skill_id);
+			// Add percent of the base damage to the damage
+			wd->damage += (wd->basedamage * sc->getSCE(SC_WATK_ELEMENT)->val2) / 100;
+		}
+	}
 #endif
+	// Cap damage to 0
+	if (battle_config.attr_recover == 0)
+		battle_min_damage(*wd, *src, skill_id, 0);
+}
 
 /*==================================
  * Calculate weapon mastery damages
@@ -3727,13 +3847,10 @@ static void battle_calc_attack_masteries(struct Damage* wd, struct block_list *s
 
 	if (sd) {
 		wd->basedamage = battle_addmastery(sd, target, wd->basedamage, 0);
-		if (is_attack_left_handed(src, skill_id)) {
-			wd->basedamage2 = battle_addmastery(sd, target, wd->basedamage2, 1);
-		}
 	}
 
-	// Grand Cross is confirmed to be affected by refine bonus but not masteries
-	if (sd && battle_skill_stacks_masteries_vvs(skill_id) && skill_id != CR_GRANDCROSS)
+	// Check if mastery damage applies to current skill
+	if (sd && battle_skill_stacks_masteries_vvs(skill_id, 0))
 	{	//Add mastery damage
 		uint16 skill;
 
@@ -3754,12 +3871,10 @@ static void battle_calc_attack_masteries(struct Damage* wd, struct block_list *s
 			ATK_ADD(wd->masteryAtk, wd->masteryAtk2, 15 * skill_lv);
 		if (skill_id != MC_CARTREVOLUTION && pc_checkskill(sd, BS_HILTBINDING) > 0)
 			ATK_ADD(wd->masteryAtk, wd->masteryAtk2, 4);
-		if (skill_id != CR_SHIELDBOOMERANG)
-			ATK_ADD2(wd->masteryAtk, wd->masteryAtk2, ((wd->div_ < 1) ? 1 : wd->div_) * sd->right_weapon.star, ((wd->div_ < 1) ? 1 : wd->div_) * sd->left_weapon.star);
-		ATK_ADD(wd->masteryAtk, wd->masteryAtk2, ((wd->div_ < 1) ? 1 : wd->div_) * sd->spiritball * 3);
-#else
-		if (skill_id == TF_POISON)
-			ATK_ADD(wd->damage, wd->damage2, 15 * skill_lv);
+		// Star Crumb bonus damage
+		ATK_ADD2(wd->masteryAtk, wd->masteryAtk2, sd->right_weapon.star, sd->left_weapon.star);
+		// Spirit Sphere bonus damage
+		ATK_ADD(wd->masteryAtk, wd->masteryAtk2, battle_get_spiritball_damage(*wd, *src, skill_id));
 #endif
 
 		if (skill_id == NJ_SYURIKEN && (skill = pc_checkskill(sd,NJ_TOBIDOUGU)) > 0) { // !TODO: Confirm new mastery formula
@@ -3900,7 +4015,10 @@ static void battle_calc_skill_base_damage(struct Damage* wd, struct block_list *
 	map_session_data *sd = BL_CAST(BL_PC, src);
 	map_session_data *tsd = BL_CAST(BL_PC, target);
 
-	uint16 i, bflag = BDMG_NONE;
+	uint16 bflag = BDMG_NONE;
+#ifndef RENEWAL
+	uint16 i;
+#endif
 	std::bitset<NK_MAX> nk = battle_skill_get_damage_properties(skill_id, wd->miscflag);
 
 	switch (skill_id) {	//Calc base damage according to skill
@@ -4069,7 +4187,6 @@ static void battle_calc_skill_base_damage(struct Damage* wd, struct block_list *
 			// Pre-renewal exclusive flags
 			if (is_skill_using_arrow(src, skill_id)) bflag |= BDMG_ARROW;
 			if (skill_id == HW_MAGICCRASHER) bflag |= BDMG_MAGIC;
-			if (skill_id == MO_EXTREMITYFIST) bflag |= BDMG_NOSIZE;
 			if (sc && sc->getSCE(SC_WEAPONPERFECTION)) bflag |= BDMG_NOSIZE;
 			if (is_skill_using_arrow(src, skill_id) && sd) {
 				switch(sd->status.weapon) {
@@ -4316,49 +4433,33 @@ static void battle_calc_multi_attack(struct Damage* wd, struct block_list *src,s
  * @param sc: Object's status change information
  * @return atkpercent with cap_value(watk,0,USHRT_MAX)
  */
-static unsigned short battle_get_atkpercent(struct block_list* bl, uint16 skill_id, status_change* sc)
+static unsigned short battle_get_atkpercent(struct block_list& bl, uint16 skill_id, status_change& sc)
 {
-	//These skills are not affected by ATKpercent
-	switch (skill_id) {
-#ifndef RENEWAL
-	// Need to be confirmed for renewal as masteries have been coded to apply to those two in renewal
-	case PA_SHIELDCHAIN:
-	case CR_SHIELDBOOMERANG:
-#endif
-	case AM_ACIDTERROR:
-	case CR_GRANDCROSS:
-	case NPC_GRANDDARKNESS:
-	case MO_INVESTIGATE:
-	case MO_EXTREMITYFIST:
-	case PA_SACRIFICE:
-	case NPC_DRAGONBREATH:
-	case RK_DRAGONBREATH:
-	case RK_DRAGONBREATH_WATER:
+	if (!battle_skill_stacks_masteries_vvs(skill_id, 0))
 		return 100;
-	}
 
 	int atkpercent = 100;
 
-	if (sc->getSCE(SC_CURSE))
+	if (sc.getSCE(SC_CURSE))
 		atkpercent -= 25;
-	if (sc->getSCE(SC_PROVOKE))
-		atkpercent += sc->getSCE(SC_PROVOKE)->val2;
-	if (sc->getSCE(SC_STRIPWEAPON) && bl->type != BL_PC)
-		atkpercent -= sc->getSCE(SC_STRIPWEAPON)->val2;
-	if (sc->getSCE(SC_CONCENTRATION))
-		atkpercent += sc->getSCE(SC_CONCENTRATION)->val2;
-	if (sc->getSCE(SC_TRUESIGHT))
-		atkpercent += 2 * sc->getSCE(SC_TRUESIGHT)->val1;
-	if (sc->getSCE(SC_JOINTBEAT) && sc->getSCE(SC_JOINTBEAT)->val2 & BREAK_WAIST)
+	if (sc.getSCE(SC_PROVOKE))
+		atkpercent += sc.getSCE(SC_PROVOKE)->val2;
+	if (sc.getSCE(SC_STRIPWEAPON) && bl.type != BL_PC)
+		atkpercent -= sc.getSCE(SC_STRIPWEAPON)->val2;
+	if (sc.getSCE(SC_CONCENTRATION))
+		atkpercent += sc.getSCE(SC_CONCENTRATION)->val2;
+	if (sc.getSCE(SC_TRUESIGHT))
+		atkpercent += 2 * sc.getSCE(SC_TRUESIGHT)->val1;
+	if (sc.getSCE(SC_JOINTBEAT) && sc.getSCE(SC_JOINTBEAT)->val2 & BREAK_WAIST)
 		atkpercent -= 25;
-	if (sc->getSCE(SC_INCATKRATE))
-		atkpercent += sc->getSCE(SC_INCATKRATE)->val1;
-	if (sc->getSCE(SC_SKE))
+	if (sc.getSCE(SC_INCATKRATE))
+		atkpercent += sc.getSCE(SC_INCATKRATE)->val1;
+	if (sc.getSCE(SC_SKE))
 		atkpercent += 300;
-	if (sc->getSCE(SC_BLOODLUST))
-		atkpercent += sc->getSCE(SC_BLOODLUST)->val2;
-	if (sc->getSCE(SC_FLEET))
-		atkpercent += sc->getSCE(SC_FLEET)->val3;
+	if (sc.getSCE(SC_BLOODLUST))
+		atkpercent += sc.getSCE(SC_BLOODLUST)->val2;
+	if (sc.getSCE(SC_FLEET))
+		atkpercent += sc.getSCE(SC_FLEET)->val3;
 
 	/* Only few selected skills should use this function, DO NOT ADD any that are not caused by the skills listed below
 	* TODO:
@@ -4396,7 +4497,7 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list *
 	if(sc && skill_id != PA_SACRIFICE) {
 #ifdef RENEWAL
 		//ATK percent modifier (in renewal, it's applied before the skillratio)
-		skillratio = battle_get_atkpercent(src, skill_id, sc);
+		skillratio = battle_get_atkpercent(*src, skill_id, *sc);
 #endif
 		if(sc->getSCE(SC_OVERTHRUST))
 			skillratio += sc->getSCE(SC_OVERTHRUST)->val3;
@@ -4635,7 +4736,7 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list *
 #endif
 			break;
 		case MO_EXTREMITYFIST:
-			skillratio += 100 * (7 + sstatus->sp / 10);			
+			skillratio += 700 + sstatus->sp * 10;
 #ifdef RENEWAL
 			if (wd->miscflag&1)
 				skillratio *= 2; // More than 5 spirit balls active
@@ -6152,6 +6253,19 @@ static int64 battle_calc_skill_constant_addition(struct Damage* wd, struct block
 		case MO_EXTREMITYFIST:
 			atk = 250 + 150 * skill_lv;
 			break;
+		case PA_SHIELDCHAIN:
+			if (sd) {
+				short index = sd->equip_index[EQI_HAND_L];
+				// Bonus damage: [max(100, Random(100, 0.7*weight + pow(skill level + refine)))]
+				if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_ARMOR) {
+					// First calculate the random part of the bonus
+					int bonus = (7 * sd->inventory_data[index]->weight) / 100;
+					bonus += static_cast<decltype(bonus)>(pow(skill_lv + sd->inventory.u.items_inventory[index].refine, 2));
+					// Now get a random value between 100 and the random part
+					atk = max(100, rnd_value(100, bonus));
+				}
+			}
+			break;
 #ifndef RENEWAL
 		case GS_MAGICALBULLET:
 			if (sstatus->matk_max > sstatus->matk_min)
@@ -6232,8 +6346,8 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
 			ATK_ADD(wd->equipAtk, wd->equipAtk2, sc->getSCE(SC_GATLINGFEVER)->val3);
 #else
 		//ATK percent modifier (in pre-renewal, it's applied multiplicatively after the skill ratio)
-		ATK_RATE(wd->damage, wd->damage2, battle_get_atkpercent(src, skill_id, sc));
-		ATK_RATE(wd->basedamage, wd->basedamage2, battle_get_atkpercent(src, 0, sc));
+		ATK_RATE(wd->damage, wd->damage2, battle_get_atkpercent(*src, skill_id, *sc));
+		ATK_RATER(wd->basedamage, battle_get_atkpercent(*src, 0, *sc));
 #endif
 		if (sc->getSCE(SC_SPIRIT)) {
 			if (skill_id == AS_SONICBLOW && sc->getSCE(SC_SPIRIT)->val2 == SL_ASSASIN) {
@@ -6250,6 +6364,7 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
 		if (sc->getSCE(SC_EDP)) {
 			switch(skill_id) {
 				// Renewal: Venom Splasher, Meteor Assault, Grimtooth and Venom Knife ignore EDP
+				case TF_SPRINKLESAND:
 				case AS_SPLASHER:
 				case ASC_METEORASSAULT:
 				case AS_GRIMTOOTH:
@@ -6544,34 +6659,15 @@ static void battle_calc_defense_reduction(struct Damage* wd, struct block_list *
 			attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_R) ?100:(is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) ? (int64)is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R)*(def1+vit_def) : (100-def1)),
 			attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_L) ?100:(is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L) ? (int64)is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L)*(def1+vit_def) : (100-def1))
 		);
-		ATK_RATE(wd->basedamage, wd->basedamage2, 100 - def1);
+		ATK_RATER(wd->basedamage, 100 - def1);
 		ATK_ADD2(wd->damage, wd->damage2,
 			attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_R) || is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) ?0:-vit_def,
 			attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_L) || is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L) ?0:-vit_def
 		);
-		ATK_ADD(wd->basedamage, wd->basedamage2, -vit_def);
+		wd->basedamage -= vit_def;
 #endif
 }
 
-
-/**
- * Cap both damage and basedamage of damage struct to a minimum value
- * @param wd: Weapon damage structure
- * @param src: Source of the attack
- * @param skill_id: Skill ID of the skill used by source
- * @param min: Minimum value to which damage should be capped
- */
-static void battle_min_damage(struct Damage* wd, struct block_list* src, uint16 skill_id, int64 min) {
-	if (is_attack_right_handed(src, skill_id)) {
-		wd->damage = cap_value(wd->damage, min, INT64_MAX);
-		wd->basedamage = cap_value(wd->basedamage, min, INT64_MAX);
-	}
-	if (is_attack_left_handed(src, skill_id)) {
-		wd->damage2 = cap_value(wd->damage2, min, INT64_MAX);
-		wd->basedamage2 = cap_value(wd->basedamage2, min, INT64_MAX);
-	}
-}
-
 /*====================================
  * Modifiers ignoring DEF
  *------------------------------------
@@ -6590,33 +6686,19 @@ static void battle_calc_attack_post_defense(struct Damage* wd, struct block_list
 #ifndef RENEWAL
 	//Refine bonus
 	if (sd) {
-		if (battle_skill_stacks_masteries_vvs(skill_id)) {
+		if (battle_skill_stacks_masteries_vvs(skill_id, 1)) {
 			ATK_ADD2(wd->damage, wd->damage2, sstatus->rhw.atk2, sstatus->lhw.atk2);
 		}
-		ATK_ADD2(wd->basedamage, wd->basedamage2, sstatus->rhw.atk2, sstatus->lhw.atk2);
+		wd->basedamage += sstatus->rhw.atk2;
 	}
 
 	//After DEF reduction, damage can be negative, refine bonus works against that value
 	//After refinement bonus was applied, damage is capped to 1, then masteries are applied
-	battle_min_damage(wd, src, skill_id, 1);
+	battle_min_damage(*wd, *src, skill_id, 1);
 
 	battle_calc_attack_masteries(wd, src, target, skill_id, skill_lv);
 #endif
 	if (sc) { // SC skill damages
-#ifndef RENEWAL
-		if (sc->getSCE(SC_EDP)) {
-			switch (skill_id) {
-				// Pre-Renewal: Soul Breaker, Venom Splasher, Meteor Assault and Soul Breaker ignore EDP
-			case AS_SPLASHER:
-			case ASC_METEORASSAULT:
-			case ASC_BREAKER:
-				break; // skills above have no effect with EDP
-			default:
-				ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_EDP)->val3);
-				break;
-			}
-		}
-#endif
 		if (sc->getSCE(SC_AURABLADE)
 #ifndef RENEWAL
 			&& skill_id != LK_SPIRALPIERCE && skill_id != ML_SPIRALPIERCE
@@ -6631,7 +6713,7 @@ static void battle_calc_attack_post_defense(struct Damage* wd, struct block_list
 	}
 
 	//Set to min of 1
-	battle_min_damage(wd, src, skill_id, 1);
+	battle_min_damage(*wd, *src, skill_id, 1);
 
 #ifdef RENEWAL
 	switch (skill_id) {
@@ -6977,7 +7059,7 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
 	wd.flag = BF_WEAPON; //Initial Flag
 	wd.flag |= (skill_id||wd.miscflag)?BF_SKILL:BF_NORMAL; // Baphomet card's splash damage is counted as a skill. [Inkfish]
 	wd.isspdamage = false;
-	wd.damage = wd.damage2 = wd.basedamage = wd.basedamage2 =
+	wd.damage = wd.damage2 = wd.basedamage =
 #ifdef RENEWAL
 	wd.statusAtk = wd.statusAtk2 = wd.equipAtk = wd.equipAtk2 = wd.weaponAtk = wd.weaponAtk2 = wd.masteryAtk = wd.masteryAtk2 =
 	wd.percentAtk = wd.percentAtk2 =
@@ -7261,7 +7343,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 		// First call function with skill_id 0 to get base damage of a normal attack
 		battle_calc_skill_base_damage(&wd, src, target, 0, 0); // base damage
 		wd.basedamage = wd.damage;
-		wd.basedamage2 = wd.damage2;
 		// Now get actual skill damage
 		if (skill_id != 0)
 			battle_calc_skill_base_damage(&wd, src, target, skill_id, skill_lv); // base skill damage
@@ -7533,28 +7614,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 			ATK_ADD(wd.damage, wd.damage2, skill * 2);
 		if (skill_id == GS_GROUNDDRIFT)
 			ATK_ADD(wd.damage, wd.damage2, 50 * skill_lv);
-		if (skill_id != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus.
-			ATK_ADD2(wd.damage, wd.damage2, ((wd.div_ < 1) ? 1 : wd.div_) * sd->right_weapon.star, ((wd.div_ < 1) ? 1 : wd.div_) * sd->left_weapon.star);
 		if (skill_id != MC_CARTREVOLUTION && pc_checkskill(sd, BS_HILTBINDING) > 0)
 			ATK_ADD(wd.damage, wd.damage2, 4);
-		if (skill_id == MO_FINGEROFFENSIVE) { //Need to calculate number of Spirit Balls you had before cast
-			ATK_ADD(wd.damage, wd.damage2, (wd.div_ + sd->spiritball) * 3);
-		} else if (skill_id != MO_INVESTIGATE)
-			ATK_ADD(wd.damage, wd.damage2, ((wd.div_ < 1) ? 1 : wd.div_) * sd->spiritball * 3);
-#endif
-		if (sd && skill_id == PA_SHIELDCHAIN) { //Rapid Smiting has a unique mastery bonus
-			short index = sd->equip_index[EQI_HAND_L];
-			//The bonus is [max(100, Random(100, 0.7*weight + pow(skill level + refine)))]
-			if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_ARMOR) {
-				//First calculate the random part of the bonus
-				int bonus = (7 * sd->inventory_data[index]->weight) / 100;
-				bonus += static_cast<decltype(bonus)>(pow(skill_lv + sd->inventory.u.items_inventory[index].refine, 2));
-				//Now get a random value between 100 and the random part
-				bonus = max(100, rnd_value(100, bonus));
-				ATK_ADD(wd.damage, wd.damage2, bonus);
-			}
-		}
-#ifndef RENEWAL
+
 		//Card Fix for attacker (sd), 2 is added to the "left" flag meaning "attacker cards only"
 		switch(skill_id) {
 			case RK_DRAGONBREATH:

+ 2 - 3
src/map/battle.hpp

@@ -36,7 +36,7 @@ enum e_base_damage_flag : uint16 {
 	BDMG_CRIT	= 0x0001, /// Critical hit damage
 	BDMG_ARROW  = 0x0002, /// Add arrow attack and use ranged damage formula
 	BDMG_MAGIC  = 0x0004, /// Use MATK for base damage (e.g. Magic Crasher)
-	BDMG_NOSIZE = 0x0008, /// Skip target size adjustment (e.g. Weapon Perfection, Extremity Fist)
+	BDMG_NOSIZE = 0x0008, /// Skip target size adjustment (e.g. Weapon Perfection)
 	BDMG_THROW  = 0x0010, /// Arrow attack should use melee damage formula (e.g., shuriken, kunai and venom knives)
 };
 
@@ -86,8 +86,7 @@ struct Damage {
 #endif
 	int64 damage, /// Right hand damage
 		damage2, /// Left hand damage
-		basedamage, /// Right hand base damage after def reduction
-		basedamage2; /// Left hand base damage after def reduction
+		basedamage; /// Right hand damage that a normal attack would deal
 	enum e_damage_type type; /// Check clif_damage for type
 	short div_; /// Number of hit
 	int amotion,

+ 0 - 2
src/map/skill.cpp

@@ -17588,9 +17588,7 @@ bool skill_check_condition_castbegin(map_session_data* sd, uint16 skill_id, uint
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 				return false;
 			}
-#ifdef RENEWAL
 			sd->spiritball_old = sd->spiritball;
-#endif
 			break;
 		case TK_MISSION:
 			if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON ) { // Cannot be used by Non-Taekwon classes