Browse Source

Enchant Deadly Poison / Magnum Break Damage Bonus and Durations (#8191)

- Added missing EDP 25% poison damage bonus (it was already fixed in renewal but applies to pre-re too), that works identical to the 20% fire damage bonus from Magnum Break; both buffs overwrite each other
- The damage on which EDP/Magnum Break is based on now considers defense reduction, passive mastery, refine and true sight
- The EDP/Magnum Break damage bonus now considers spirit sphere damage, but the spirit sphere part is non-elemental; both are added together before they get rounded down
- The EDP/Magnum Break damage bonus can now become negative through attribute table and is dispelled on logout
- The EDP/Magnum Break damage bonus no longer applies when damage is already 0 or lower beforehand
- EDP is now applied after defense, refine and mastery
- Refine bonus now is applied before mastery and between both the damage is capped to 1
- Envenom's fixed damage bonus is now a mastery bonus (was already fixed in renewal but applies to pre-re too) and thus increased by EDP
- Fixed damage interaction between MO_FINGEROFFENSIVE (Throw Spirit Sphere) with Magnum Break, refine and spirit spheres
- Fixed Magnum Break costing HP; it requires HP but does not consume it
- Fixed Sonic Blow's damage formula (300%+50%*skill_lv) and how the bonus from Sonic Acceleration works (+10% to ratio)
- Poison/DPoison now reduce hard DEF vs. monsters by 25%, but the DEF reduction no longer stacks
- Fixed DPoison's duration (60s base duration on all levels)
- Fixed Enchant Deadly Poison duration in Renewal
- Fixed chance of DPoison to be inflicted (3% level 1+2, 4% level 3+4, 5% level 5)
- Fixed negative resistances not increasing the chance of a status change to occur (it's only capped to 0 in renewal)
Playtester 1 năm trước cách đây
mục cha
commit
861832ec27

+ 1 - 11
db/pre-re/skill_db.yml

@@ -10233,17 +10233,7 @@ Body:
         Time: 55000
         Time: 55000
       - Level: 5
       - Level: 5
         Time: 60000
         Time: 60000
-    Duration2:
-      - Level: 1
-        Time: 20000
-      - Level: 2
-        Time: 30000
-      - Level: 3
-        Time: 40000
-      - Level: 4
-        Time: 50000
-      - Level: 5
-        Time: 60000
+    Duration2: 60000
     Requires:
     Requires:
       SpCost:
       SpCost:
         - Level: 1
         - Level: 1

+ 6 - 0
db/pre-re/status.yml

@@ -162,6 +162,7 @@ Body:
   - Status: Poison
   - Status: Poison
     DurationLookup: NPC_POISON
     DurationLookup: NPC_POISON
     CalcFlags:
     CalcFlags:
+      Def: true
       Def2: true
       Def2: true
       Regen: true
       Regen: true
     Opt2:
     Opt2:
@@ -252,6 +253,7 @@ Body:
   - Status: Dpoison
   - Status: Dpoison
     DurationLookup: NPC_POISON
     DurationLookup: NPC_POISON
     CalcFlags:
     CalcFlags:
+      Def: true
       Def2: true
       Def2: true
       Regen: true
       Regen: true
     Opt2:
     Opt2:
@@ -1121,6 +1123,10 @@ Body:
       NoClearance: true
       NoClearance: true
   - Status: Watk_Element
   - Status: Watk_Element
     DurationLookup: MS_MAGNUM
     DurationLookup: MS_MAGNUM
+    Flags:
+      NoSave: true
+    EndOnStart:
+      Watk_Element: true
   - Status: Armor
   - Status: Armor
     DurationLookup: NPC_DEFENDER
     DurationLookup: NPC_DEFENDER
   - Status: Armor_Element_Water
   - Status: Armor_Element_Water

+ 3 - 3
db/re/skill_db.yml

@@ -10486,11 +10486,11 @@ Body:
       - Level: 1
       - Level: 1
         Time: 40000
         Time: 40000
       - Level: 2
       - Level: 2
-        Time: 45000
+        Time: 60000
       - Level: 3
       - Level: 3
-        Time: 50000
+        Time: 80000
       - Level: 4
       - Level: 4
-        Time: 55000
+        Time: 100000
       - Level: 5
       - Level: 5
         Time: 120000
         Time: 120000
     Duration2:
     Duration2:

+ 6 - 0
db/re/status.yml

@@ -165,6 +165,7 @@ Body:
   - Status: Poison
   - Status: Poison
     DurationLookup: NPC_POISON
     DurationLookup: NPC_POISON
     CalcFlags:
     CalcFlags:
+      Def: true
       Def2: true
       Def2: true
       Regen: true
       Regen: true
     Opt2:
     Opt2:
@@ -261,6 +262,7 @@ Body:
   - Status: Dpoison
   - Status: Dpoison
     DurationLookup: NPC_POISON
     DurationLookup: NPC_POISON
     CalcFlags:
     CalcFlags:
+      Def: true
       Def2: true
       Def2: true
       Regen: true
       Regen: true
     Opt2:
     Opt2:
@@ -1139,6 +1141,10 @@ Body:
       NoClearance: true
       NoClearance: true
   - Status: Watk_Element
   - Status: Watk_Element
     DurationLookup: MS_MAGNUM
     DurationLookup: MS_MAGNUM
+    Flags:
+      NoSave: true
+    EndOnStart:
+      Watk_Element: true
   - Status: Armor
   - Status: Armor
     DurationLookup: NPC_DEFENDER
     DurationLookup: NPC_DEFENDER
   - Status: Armor_Element_Water
   - Status: Armor_Element_Water

+ 104 - 56
src/map/battle.cpp

@@ -421,10 +421,10 @@ int battle_delay_damage(t_tick tick, int amotion, struct block_list *src, struct
  * @param atk_elem
  * @param atk_elem
  * @param def_type
  * @param def_type
  * @param def_lv
  * @param def_lv
- * @param flag
+ * @param flag 0x1 = allow to return negative values even if config for healing through negative resist is disabled
  * @return damage
  * @return damage
  */
  */
-int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 damage,int atk_elem,int def_type, int def_lv)
+int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 damage,int atk_elem,int def_type, int def_lv, int flag)
 {
 {
 	status_change *sc = NULL, *tsc = NULL;
 	status_change *sc = NULL, *tsc = NULL;
 	int ratio;
 	int ratio;
@@ -631,7 +631,7 @@ int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 d
 #endif
 #endif
 	}
 	}
 
 
-	if (battle_config.attr_recover == 0 && ratio < 0)
+	if (battle_config.attr_recover == 0 && !(flag & 1) && ratio < 0)
 		ratio = 0;
 		ratio = 0;
 
 
 #ifdef RENEWAL
 #ifdef RENEWAL
@@ -2444,8 +2444,7 @@ static int battle_calc_base_weapon_attack(struct block_list *src, struct status_
 #endif
 #endif
 
 
 /*==========================================
 /*==========================================
- * Calculates the standard damage of a normal attack assuming it hits,
- * it calculates nothing extra fancy, is needed for magnum break's WATK_ELEMENT bonus. [Skotlex]
+ * Calculates the standard damage of a normal attack assuming it hits
  * This applies to pre-renewal and non-sd in renewal
  * This applies to pre-renewal and non-sd in renewal
  *------------------------------------------
  *------------------------------------------
  * Pass damage2 as NULL to not calc it.
  * Pass damage2 as NULL to not calc it.
@@ -3656,19 +3655,30 @@ static void battle_calc_element_damage(struct Damage* wd, struct block_list *src
 				break;
 				break;
 		}
 		}
 
 
-#ifdef RENEWAL
-		if (sd == nullptr) { // Only monsters have a single ATK for element, in pre-renewal we also apply element to entire ATK on players [helvetica]
-#endif
+#ifndef RENEWAL
+		if (sd && (wd->damage > 0 || wd->damage2 > 0)) { // Applies only to player damage, monsters and mercenaries don't get this damage boost
 			if (sc && sc->getSCE(SC_WATK_ELEMENT)) { // Descriptions indicate this means adding a percent of a normal attack in another element [Skotlex]
 			if (sc && sc->getSCE(SC_WATK_ELEMENT)) { // Descriptions indicate this means adding a percent of a normal attack in another element [Skotlex]
-				int64 damage = battle_calc_base_damage(src, sstatus, &sstatus->rhw, sc, tstatus->size, (is_skill_using_arrow(src, skill_id) ? 2 : 0)) * sc->getSCE(SC_WATK_ELEMENT)->val2 / 100;
-
-				wd->damage += battle_attr_fix(src, target, damage, sc->getSCE(SC_WATK_ELEMENT)->val1, tstatus->def_ele, tstatus->ele_lv);
+				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) { //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)) {
 				if (is_attack_left_handed(src, skill_id)) {
-					damage = battle_calc_base_damage(src, sstatus, &sstatus->lhw, sc, tstatus->size, (is_skill_using_arrow(src, skill_id) ? 2 : 0)) * sc->getSCE(SC_WATK_ELEMENT)->val2 / 100;
-					wd->damage2 += battle_attr_fix(src, target, damage, sc->getSCE(SC_WATK_ELEMENT)->val1, tstatus->def_ele, tstatus->ele_lv);
+					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);
 				}
 				}
 			}
 			}
-#ifdef RENEWAL
 		}
 		}
 #endif
 #endif
 	}
 	}
@@ -3718,11 +3728,13 @@ static void battle_calc_attack_masteries(struct Damage* wd, struct block_list *s
 		uint16 skill;
 		uint16 skill;
 
 
 		wd->damage = battle_addmastery(sd,target,wd->damage,0);
 		wd->damage = battle_addmastery(sd,target,wd->damage,0);
+		wd->basedamage = battle_addmastery(sd, target, wd->basedamage, 0);
 #ifdef RENEWAL
 #ifdef RENEWAL
 		wd->masteryAtk = battle_addmastery(sd,target,wd->weaponAtk,0);
 		wd->masteryAtk = battle_addmastery(sd,target,wd->weaponAtk,0);
 #endif
 #endif
 		if (is_attack_left_handed(src, skill_id)) {
 		if (is_attack_left_handed(src, skill_id)) {
 			wd->damage2 = battle_addmastery(sd,target,wd->damage2,1);
 			wd->damage2 = battle_addmastery(sd,target,wd->damage2,1);
+			wd->basedamage2 = battle_addmastery(sd, target, wd->basedamage2, 1);
 #ifdef RENEWAL
 #ifdef RENEWAL
 			wd->masteryAtk2 = battle_addmastery(sd,target,wd->weaponAtk2,1);
 			wd->masteryAtk2 = battle_addmastery(sd,target,wd->weaponAtk2,1);
 #endif
 #endif
@@ -3737,6 +3749,9 @@ static void battle_calc_attack_masteries(struct Damage* wd, struct block_list *s
 		if (skill_id != CR_SHIELDBOOMERANG)
 		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_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);
 		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);
 #endif
 #endif
 
 
 		if (skill_id == NJ_SYURIKEN && (skill = pc_checkskill(sd,NJ_TOBIDOUGU)) > 0) { // !TODO: Confirm new mastery formula
 		if (skill_id == NJ_SYURIKEN && (skill = pc_checkskill(sd,NJ_TOBIDOUGU)) > 0) { // !TODO: Confirm new mastery formula
@@ -4423,7 +4438,9 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list *
 			if (tstatus->hp < (tstatus->max_hp / 2))
 			if (tstatus->hp < (tstatus->max_hp / 2))
 				skillratio += skillratio / 2;
 				skillratio += skillratio / 2;
 #else
 #else
-			skillratio += 300 + 40 * skill_lv;
+			skillratio += 200 + 50 * skill_lv;
+			if (sd && pc_checkskill(sd, AS_SONICACCEL) > 0)
+				skillratio += skillratio / 10;
 #endif
 #endif
 			break;
 			break;
 		case TF_SPRINKLESAND:
 		case TF_SPRINKLESAND:
@@ -6129,8 +6146,10 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
 		if (sc->getSCE(SC_GATLINGFEVER))
 		if (sc->getSCE(SC_GATLINGFEVER))
 			ATK_ADD(wd->equipAtk, wd->equipAtk2, sc->getSCE(SC_GATLINGFEVER)->val3);
 			ATK_ADD(wd->equipAtk, wd->equipAtk2, sc->getSCE(SC_GATLINGFEVER)->val3);
 #else
 #else
-		if (sc->getSCE(SC_TRUESIGHT))
+		if (sc->getSCE(SC_TRUESIGHT)) {
 			ATK_ADDRATE(wd->damage, wd->damage2, 2 * sc->getSCE(SC_TRUESIGHT)->val1);
 			ATK_ADDRATE(wd->damage, wd->damage2, 2 * sc->getSCE(SC_TRUESIGHT)->val1);
+			ATK_ADDRATE(wd->basedamage, wd->basedamage2, 2 * sc->getSCE(SC_TRUESIGHT)->val1);
+		}
 #endif
 #endif
 		if (sc->getSCE(SC_SPIRIT)) {
 		if (sc->getSCE(SC_SPIRIT)) {
 			if (skill_id == AS_SONICBLOW && sc->getSCE(SC_SPIRIT)->val2 == SL_ASSASIN) {
 			if (skill_id == AS_SONICBLOW && sc->getSCE(SC_SPIRIT)->val2 == SL_ASSASIN) {
@@ -6143,22 +6162,16 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
 		}
 		}
 		if (sc->getSCE(SC_GT_CHANGE))
 		if (sc->getSCE(SC_GT_CHANGE))
 			ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_GT_CHANGE)->val1);
 			ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_GT_CHANGE)->val1);
+#ifdef RENEWAL
 		if (sc->getSCE(SC_EDP)) {
 		if (sc->getSCE(SC_EDP)) {
 			switch(skill_id) {
 			switch(skill_id) {
+				// Renewal: Venom Splasher, Meteor Assault, Grimtooth and Venom Knife ignore EDP
 				case AS_SPLASHER:
 				case AS_SPLASHER:
 				case ASC_METEORASSAULT:
 				case ASC_METEORASSAULT:
-				// Pre-Renewal only: Soul Breaker ignores EDP
-				// Renewal only: Grimtooth and Venom Knife ignore EDP
-				// Both: Venom Splasher and Meteor Assault ignore EDP [helvetica]
-#ifndef RENEWAL
-				case ASC_BREAKER:
-#else
 				case AS_GRIMTOOTH:
 				case AS_GRIMTOOTH:
 				case AS_VENOMKNIFE:
 				case AS_VENOMKNIFE:
-#endif
 					break; // skills above have no effect with EDP
 					break; // skills above have no effect with EDP
 
 
-#ifdef RENEWAL
 				default: // fall through to apply EDP bonuses
 				default: // fall through to apply EDP bonuses
 					// Renewal EDP formula [helvetica]
 					// Renewal EDP formula [helvetica]
 					// weapon atk * (2.5 + (edp level * .3))
 					// weapon atk * (2.5 + (edp level * .3))
@@ -6166,13 +6179,9 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
 					ATK_RATE(wd->weaponAtk, wd->weaponAtk2, 250 + (sc->getSCE(SC_EDP)->val1 * 30));
 					ATK_RATE(wd->weaponAtk, wd->weaponAtk2, 250 + (sc->getSCE(SC_EDP)->val1 * 30));
 					ATK_RATE(wd->equipAtk, wd->equipAtk2, 250 + (sc->getSCE(SC_EDP)->val1 * 30));
 					ATK_RATE(wd->equipAtk, wd->equipAtk2, 250 + (sc->getSCE(SC_EDP)->val1 * 30));
 					break;
 					break;
-#else
-				default:
-					ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_EDP)->val3);
-
-#endif
 			}
 			}
 		}
 		}
+#endif
 		if (sc->getSCE(SC_DANCEWITHWUG)) {
 		if (sc->getSCE(SC_DANCEWITHWUG)) {
 			if (skill_get_inf2(skill_id, INF2_INCREASEDANCEWITHWUGDAMAGE)) {
 			if (skill_get_inf2(skill_id, INF2_INCREASEDANCEWITHWUGDAMAGE)) {
 				ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_DANCEWITHWUG)->val1 * 10 * battle_calc_chorusbonus(sd));
 				ATK_ADDRATE(wd->damage, wd->damage2, sc->getSCE(SC_DANCEWITHWUG)->val1 * 10 * battle_calc_chorusbonus(sd));
@@ -6452,13 +6461,40 @@ 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_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))
 			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_RATE2(wd->basedamage, wd->basedamage2,
+			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_ADD2(wd->damage, wd->damage2,
 		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_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
 			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_ADD2(wd->basedamage, wd->basedamage2,
+			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
+		);
 #endif
 #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)) {
+		cap_value(wd->damage, min, INT64_MAX);
+		cap_value(wd->basedamage, min, INT64_MAX);
+	}
+	if (is_attack_left_handed(src, skill_id)) {
+		cap_value(wd->damage2, min, INT64_MAX);
+		cap_value(wd->basedamage2, min, INT64_MAX);
+	}
+}
+
 /*====================================
 /*====================================
  * Modifiers ignoring DEF
  * Modifiers ignoring DEF
  *------------------------------------
  *------------------------------------
@@ -6474,12 +6510,39 @@ static void battle_calc_attack_post_defense(struct Damage* wd, struct block_list
 	struct status_data *sstatus = status_get_status_data(src);
 	struct status_data *sstatus = status_get_status_data(src);
 
 
 	// Post skill/vit reduction damage increases
 	// Post skill/vit reduction damage increases
-	if( sc ) { // SC skill damages
-		if(sc->getSCE(SC_AURABLADE)
 #ifndef RENEWAL
 #ifndef RENEWAL
-				&& skill_id != LK_SPIRALPIERCE && skill_id != ML_SPIRALPIERCE
+	//Refine bonus
+	if (sd && battle_skill_stacks_masteries_vvs(skill_id) && skill_id != MO_INVESTIGATE && skill_id != MO_EXTREMITYFIST) { // Counts refine bonus multiple times
+		ATK_ADD2(wd->damage, wd->damage2, sstatus->rhw.atk2, sstatus->lhw.atk2);
+		ATK_ADD2(wd->basedamage, wd->basedamage2, sstatus->rhw.atk2, sstatus->lhw.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_calc_attack_masteries(wd, src, target, skill_id, skill_lv);
 #endif
 #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
+#endif
+			) {
 #ifdef RENEWAL
 #ifdef RENEWAL
 			ATK_ADD(wd->damage, wd->damage2, (3 + sc->getSCE(SC_AURABLADE)->val1) * status_get_lv(src)); // !TODO: Confirm formula
 			ATK_ADD(wd->damage, wd->damage2, (3 + sc->getSCE(SC_AURABLADE)->val1) * status_get_lv(src)); // !TODO: Confirm formula
 #else
 #else
@@ -6488,32 +6551,17 @@ static void battle_calc_attack_post_defense(struct Damage* wd, struct block_list
 		}
 		}
 	}
 	}
 
 
-#ifndef RENEWAL
-	battle_calc_attack_masteries(wd, src, target, skill_id, skill_lv);
-
-	//Refine bonus
-	if (sd && battle_skill_stacks_masteries_vvs(skill_id) && skill_id != MO_INVESTIGATE && skill_id != MO_EXTREMITYFIST) { // Counts refine bonus multiple times
-		if (skill_id == MO_FINGEROFFENSIVE) {
-			ATK_ADD2(wd->damage, wd->damage2, wd->div_*sstatus->rhw.atk2, wd->div_*sstatus->lhw.atk2);
-		} else {
-			ATK_ADD2(wd->damage, wd->damage2, sstatus->rhw.atk2, sstatus->lhw.atk2);
-		}
-	}
-#endif
 	//Set to min of 1
 	//Set to min of 1
-	if (is_attack_right_handed(src, skill_id) && wd->damage < 1) wd->damage = 1;
-	if (is_attack_left_handed(src, skill_id) && wd->damage2 < 1) wd->damage2 = 1;
+	battle_min_damage(wd, src, skill_id, 1);
 
 
+#ifdef RENEWAL
 	switch (skill_id) {
 	switch (skill_id) {
 		case AS_SONICBLOW:
 		case AS_SONICBLOW:
 			if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
 			if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
-#ifdef RENEWAL
 				ATK_ADDRATE(wd->damage, wd->damage2, 90);
 				ATK_ADDRATE(wd->damage, wd->damage2, 90);
-#else
-				ATK_ADDRATE(wd->damage, wd->damage2, 10);
-#endif
 			break;
 			break;
 	}
 	}
+#endif
 }
 }
 
 
 /*=================================================================================
 /*=================================================================================
@@ -6850,7 +6898,7 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block
 	wd.flag = BF_WEAPON; //Initial Flag
 	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.flag |= (skill_id||wd.miscflag)?BF_SKILL:BF_NORMAL; // Baphomet card's splash damage is counted as a skill. [Inkfish]
 	wd.isspdamage = false;
 	wd.isspdamage = false;
-	wd.damage = wd.damage2 =
+	wd.damage = wd.damage2 = wd.basedamage = wd.basedamage2 =
 #ifdef RENEWAL
 #ifdef RENEWAL
 	wd.statusAtk = wd.statusAtk2 = wd.equipAtk = wd.equipAtk2 = wd.weaponAtk = wd.weaponAtk2 = wd.masteryAtk = wd.masteryAtk2 =
 	wd.statusAtk = wd.statusAtk2 = wd.equipAtk = wd.equipAtk2 = wd.weaponAtk = wd.weaponAtk2 = wd.masteryAtk = wd.masteryAtk2 =
 	wd.percentAtk = wd.percentAtk2 =
 	wd.percentAtk = wd.percentAtk2 =
@@ -7132,6 +7180,8 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 	else if(!(infdef = is_infinite_defense(target, wd.flag))) { //no need for math against plants
 	else if(!(infdef = is_infinite_defense(target, wd.flag))) { //no need for math against plants
 
 
 		battle_calc_skill_base_damage(&wd, src, target, skill_id, skill_lv); // base skill damage
 		battle_calc_skill_base_damage(&wd, src, target, skill_id, skill_lv); // base skill damage
+		wd.basedamage = wd.damage;
+		wd.basedamage2 = wd.damage2;
 
 
 		int64 ratio = 0;
 		int64 ratio = 0;
 
 
@@ -7392,16 +7442,14 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 
 
 		if ((skill = pc_checkskill(sd, BS_WEAPONRESEARCH)) > 0)
 		if ((skill = pc_checkskill(sd, BS_WEAPONRESEARCH)) > 0)
 			ATK_ADD(wd.damage, wd.damage2, skill * 2);
 			ATK_ADD(wd.damage, wd.damage2, skill * 2);
-		if (skill_id == TF_POISON)
-			ATK_ADD(wd.damage, wd.damage2, 15 * skill_lv);
 		if (skill_id == GS_GROUNDDRIFT)
 		if (skill_id == GS_GROUNDDRIFT)
 			ATK_ADD(wd.damage, wd.damage2, 50 * skill_lv);
 			ATK_ADD(wd.damage, wd.damage2, 50 * skill_lv);
 		if (skill_id != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus.
 		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);
 			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)
 		if (skill_id != MC_CARTREVOLUTION && pc_checkskill(sd, BS_HILTBINDING) > 0)
 			ATK_ADD(wd.damage, wd.damage2, 4);
 			ATK_ADD(wd.damage, wd.damage2, 4);
-		if (skill_id == MO_FINGEROFFENSIVE) { //The finger offensive spheres on moment of attack do count. [Skotlex]
-			ATK_ADD(wd.damage, wd.damage2, ((wd.div_ < 1) ? 1 : wd.div_) * sd->spiritball_old * 3);
+		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
 		} else
 			ATK_ADD(wd.damage, wd.damage2, ((wd.div_ < 1) ? 1 : wd.div_) * sd->spiritball * 3);
 			ATK_ADD(wd.damage, wd.damage2, ((wd.div_ < 1) ? 1 : wd.div_) * sd->spiritball * 3);
 #endif
 #endif

+ 4 - 2
src/map/battle.hpp

@@ -75,7 +75,9 @@ struct Damage {
 	int64 statusAtk, statusAtk2, weaponAtk, weaponAtk2, equipAtk, equipAtk2, masteryAtk, masteryAtk2, percentAtk, percentAtk2;
 	int64 statusAtk, statusAtk2, weaponAtk, weaponAtk2, equipAtk, equipAtk2, masteryAtk, masteryAtk2, percentAtk, percentAtk2;
 #endif
 #endif
 	int64 damage, /// Right hand damage
 	int64 damage, /// Right hand damage
-		damage2; /// Left hand damage
+		damage2, /// Left hand damage
+		basedamage, /// Right hand base damage after def reduction
+		basedamage2; /// Left hand base damage after def reduction
 	enum e_damage_type type; /// Check clif_damage for type
 	enum e_damage_type type; /// Check clif_damage for type
 	short div_; /// Number of hit
 	short div_; /// Number of hit
 	int amotion,
 	int amotion,
@@ -95,7 +97,7 @@ int64 battle_calc_return_damage(struct block_list *bl, struct block_list *src, i
 
 
 void battle_drain(map_session_data *sd, struct block_list *tbl, int64 rdamage, int64 ldamage, int race, int class_);
 void battle_drain(map_session_data *sd, struct block_list *tbl, int64 rdamage, int64 ldamage, int race, int class_);
 
 
-int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 damage,int atk_elem,int def_type, int def_lv);
+int64 battle_attr_fix(struct block_list* src, struct block_list* target, int64 damage, int atk_elem, int def_type, int def_lv, int flag = 0);
 int battle_calc_cardfix(int attack_type, struct block_list *src, struct block_list *target, std::bitset<NK_MAX> nk, int s_ele, int s_ele_, int64 damage, int left, int flag);
 int battle_calc_cardfix(int attack_type, struct block_list *src, struct block_list *target, std::bitset<NK_MAX> nk, int s_ele, int s_ele_, int64 damage, int left, int flag);
 
 
 // Final calculation Damage
 // Final calculation Damage

+ 31 - 10
src/map/skill.cpp

@@ -4377,6 +4377,28 @@ static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16
 		map_foreachinallarea(npc_isnear_sub,bl->m,x - range,y - range,x + range,y + range,type,skill_id);
 		map_foreachinallarea(npc_isnear_sub,bl->m,x - range,y - range,x + range,y + range,type,skill_id);
 }
 }
 
 
+/** Apply special cases where skills require HP/SP/AP but do not consume them, then continue with consuming HP/SP/AP
+* @param bl Object from which HP/SP/AP are consumed
+* @param skill_id ID of used skill
+* @param hp Original HP requirement to use skill
+* @param sp Original SP requirement to use skill
+* @param ap Original AP requirement to use skill
+*/
+void skill_consume_hpspap(block_list* bl, uint16 skill_id, int hp, int sp, int ap)
+{
+	nullpo_retv(bl);
+
+	switch (skill_id) {
+		//Skills that require HP but do not consume them
+	case SM_MAGNUM:
+	case MS_MAGNUM:
+		hp = 0;
+		break;
+	}
+
+	status_zap(bl, hp, sp, ap);
+}
+
 /*==========================================
 /*==========================================
  * Checks that you have the requirements for casting a skill for homunculus/mercenary.
  * Checks that you have the requirements for casting a skill for homunculus/mercenary.
  * Flag:
  * Flag:
@@ -4480,7 +4502,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, uint16 skill_i
 		return 1;
 		return 1;
 
 
 	if( sp || hp )
 	if( sp || hp )
-		status_zap(bl, hp, sp);
+		skill_consume_hpspap(bl, skill_id, hp, sp, 0);
 
 
 	return 1;
 	return 1;
 }
 }
@@ -7847,9 +7869,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		clif_skill_nodamage (src,src,skill_id,skill_lv,1);
 		clif_skill_nodamage (src,src,skill_id,skill_lv,1);
 		// Initiate 20% of your damage becomes fire element.
 		// Initiate 20% of your damage becomes fire element.
 #ifdef RENEWAL
 #ifdef RENEWAL
-		sc_start4(src,src,SC_SUB_WEAPONPROPERTY,100,3,20,skill_id,0,skill_get_time2(skill_id, skill_lv));
+		sc_start4(src,src,SC_SUB_WEAPONPROPERTY,100,ELE_FIRE,20,skill_id,0,skill_get_time2(skill_id, skill_lv));
 #else
 #else
-		sc_start4(src,src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skill_id, skill_lv));
+		sc_start4(src,src,SC_WATK_ELEMENT,100,ELE_FIRE,20,0,0,skill_get_time2(skill_id, skill_lv));
 #endif
 #endif
 		break;
 		break;
 
 
@@ -7924,9 +7946,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case HW_MAGICPOWER:
 	case HW_MAGICPOWER:
 	case PF_MEMORIZE:
 	case PF_MEMORIZE:
 	case PA_SACRIFICE:
 	case PA_SACRIFICE:
-#ifndef RENEWAL
-	case ASC_EDP:
-#endif
 	case PF_DOUBLECASTING:
 	case PF_DOUBLECASTING:
 	case SG_SUN_COMFORT:
 	case SG_SUN_COMFORT:
 	case SG_MOON_COMFORT:
 	case SG_MOON_COMFORT:
@@ -8035,14 +8054,16 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, DMG_SINGLE);
 		clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, DMG_SINGLE);
 		break;
 		break;
 
 
-#ifdef RENEWAL
 	// EDP also give +25% WATK poison pseudo element to user.
 	// EDP also give +25% WATK poison pseudo element to user.
 	case ASC_EDP:
 	case ASC_EDP:
 		clif_skill_nodamage(src,bl,skill_id,skill_lv,
 		clif_skill_nodamage(src,bl,skill_id,skill_lv,
 			sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
 			sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
-		sc_start4(src,src,SC_SUB_WEAPONPROPERTY,100,5,25,skill_id,0,skill_get_time2(skill_id, skill_lv));
-		break;
+#ifdef RENEWAL
+		sc_start4(src, src, SC_SUB_WEAPONPROPERTY, 100, ELE_POISON, 25, skill_id, 0, skill_get_time(skill_id, skill_lv));
+#else
+		sc_start4(src, src, SC_WATK_ELEMENT, 100, ELE_POISON, 25, 0, 0, skill_get_time(skill_id, skill_lv));
 #endif
 #endif
+		break;
 
 
 	case LG_SHIELDSPELL:
 	case LG_SHIELDSPELL:
 		if (skill_lv == 1)
 		if (skill_lv == 1)
@@ -18648,7 +18669,7 @@ void skill_consume_requirement(map_session_data *sd, uint16 skill_id, uint16 ski
 			break;
 			break;
 		}
 		}
 		if(require.hp || require.sp || require.ap)
 		if(require.hp || require.sp || require.ap)
-			status_zap(&sd->bl, require.hp, require.sp, require.ap);
+			skill_consume_hpspap(&sd->bl, skill_id, require.hp, require.sp, require.ap);
 
 
 		if(require.spiritball > 0) { // Skills that require certain types of spheres to use
 		if(require.spiritball > 0) { // Skills that require certain types of spheres to use
 			switch (skill_id) { // Skills that require soul spheres.
 			switch (skill_id) { // Skills that require soul spheres.

+ 15 - 9
src/map/status.cpp

@@ -7678,6 +7678,8 @@ static defType status_calc_def(struct block_list *bl, status_change *sc, int def
 		def /= 2;
 		def /= 2;
 	if(sc->getSCE(SC_FREEZE))
 	if(sc->getSCE(SC_FREEZE))
 		def /= 2;
 		def /= 2;
+	if(sc->getSCE(SC_POISON) || sc->getSCE(SC_DPOISON) && bl->type != BL_PC)
+		def = def * 75 / 100; //Should round down
 	if(sc->getSCE(SC_SIGNUMCRUCIS))
 	if(sc->getSCE(SC_SIGNUMCRUCIS))
 		def -= def * sc->getSCE(SC_SIGNUMCRUCIS)->val2/100;
 		def -= def * sc->getSCE(SC_SIGNUMCRUCIS)->val2/100;
 	if(sc->getSCE(SC_CONCENTRATION))
 	if(sc->getSCE(SC_CONCENTRATION))
@@ -7773,10 +7775,8 @@ static signed short status_calc_def2(struct block_list *bl, status_change *sc, i
 	if(sc->getSCE(SC_CONCENTRATION))
 	if(sc->getSCE(SC_CONCENTRATION))
 		def2 -= def2 * sc->getSCE(SC_CONCENTRATION)->val4/100;
 		def2 -= def2 * sc->getSCE(SC_CONCENTRATION)->val4/100;
 #endif
 #endif
-	if(sc->getSCE(SC_POISON))
-		def2 -= def2 * 25/100;
-	if(sc->getSCE(SC_DPOISON))
-		def2 -= def2 * 25/100;
+	if(sc->getSCE(SC_POISON) || sc->getSCE(SC_DPOISON))
+		def2 = def2 * 75 / 100; //Should round down
 	if(sc->getSCE(SC_SKE))
 	if(sc->getSCE(SC_SKE))
 		def2 -= def2 * 50/100;
 		def2 -= def2 * 50/100;
 	if(sc->getSCE(SC_PROVOKE))
 	if(sc->getSCE(SC_PROVOKE))
@@ -9729,10 +9729,13 @@ t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_
 			sc_def = sc_def*battle_config.pc_sc_def_rate/100;
 			sc_def = sc_def*battle_config.pc_sc_def_rate/100;
 			sc_def2 = sc_def2*battle_config.pc_sc_def_rate/100;
 			sc_def2 = sc_def2*battle_config.pc_sc_def_rate/100;
 		}
 		}
-
+#ifndef RENEWAL
+		sc_def = min(sc_def, battle_config.pc_max_sc_def*100);
+		sc_def2 = min(sc_def2, battle_config.pc_max_sc_def*100);
+#else
 		sc_def = cap_value(sc_def, 0, battle_config.pc_max_sc_def*100);
 		sc_def = cap_value(sc_def, 0, battle_config.pc_max_sc_def*100);
 		sc_def2 = cap_value(sc_def2, 0, battle_config.pc_max_sc_def*100);
 		sc_def2 = cap_value(sc_def2, 0, battle_config.pc_max_sc_def*100);
-
+#endif
 		if (battle_config.pc_sc_def_rate != 100) {
 		if (battle_config.pc_sc_def_rate != 100) {
 			tick_def = tick_def*battle_config.pc_sc_def_rate/100;
 			tick_def = tick_def*battle_config.pc_sc_def_rate/100;
 			tick_def2 = tick_def2*battle_config.pc_sc_def_rate/100;
 			tick_def2 = tick_def2*battle_config.pc_sc_def_rate/100;
@@ -9742,10 +9745,13 @@ t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_
 			sc_def = sc_def*battle_config.mob_sc_def_rate/100;
 			sc_def = sc_def*battle_config.mob_sc_def_rate/100;
 			sc_def2 = sc_def2*battle_config.mob_sc_def_rate/100;
 			sc_def2 = sc_def2*battle_config.mob_sc_def_rate/100;
 		}
 		}
-
+#ifndef RENEWAL
+		sc_def = min(sc_def, battle_config.mob_max_sc_def*100);
+		sc_def2 = min(sc_def2, battle_config.mob_max_sc_def*100);
+#else
 		sc_def = cap_value(sc_def, 0, battle_config.mob_max_sc_def*100);
 		sc_def = cap_value(sc_def, 0, battle_config.mob_max_sc_def*100);
 		sc_def2 = cap_value(sc_def2, 0, battle_config.mob_max_sc_def*100);
 		sc_def2 = cap_value(sc_def2, 0, battle_config.mob_max_sc_def*100);
-
+#endif
 		if (battle_config.mob_sc_def_rate != 100) {
 		if (battle_config.mob_sc_def_rate != 100) {
 			tick_def = tick_def*battle_config.mob_sc_def_rate/100;
 			tick_def = tick_def*battle_config.mob_sc_def_rate/100;
 			tick_def2 = tick_def2*battle_config.mob_sc_def_rate/100;
 			tick_def2 = tick_def2*battle_config.mob_sc_def_rate/100;
@@ -10643,7 +10649,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			tick = INFINITE_TICK; // Duration sent to the client should be infinite
 			tick = INFINITE_TICK; // Duration sent to the client should be infinite
 			break;
 			break;
 		case SC_EDP:
 		case SC_EDP:
-			val2 = val1 + 2; // Chance to Poison enemies.
+			val2 = (val1 + 1) / 2 + 2; // Chance to Poison enemies.
 #ifndef RENEWAL
 #ifndef RENEWAL
 			val3 = 50*(val1+1); // Damage increase (+50 +50*lv%)
 			val3 = 50*(val1+1); // Damage increase (+50 +50*lv%)
 #endif
 #endif