Selaa lähdekoodia

BETA: Fixed minor problem caused pet damage to not be counted as it's owners damage.
* Fixed minor problem that caused wrong exp/sp calculations we should use total damage instead of max hp.
* Replacing ConcurentHashMap with HashMap its used only inside the calculateRewards method we don't need thread-safe map there.
* Patch by: Nos
* Reported by: rocki
* Reviewed by: UnAfraid, Zoey76

Rumen Nikiforov 12 vuotta sitten
vanhempi
sitoutus
d1c7ce2b9e

+ 44 - 77
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Party.java

@@ -18,6 +18,7 @@
  */
 package com.l2jserver.gameserver.model;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.Future;
@@ -35,10 +36,8 @@ import com.l2jserver.gameserver.datatables.SkillTable;
 import com.l2jserver.gameserver.instancemanager.DuelManager;
 import com.l2jserver.gameserver.model.actor.L2Attackable;
 import com.l2jserver.gameserver.model.actor.L2Character;
-import com.l2jserver.gameserver.model.actor.L2Playable;
 import com.l2jserver.gameserver.model.actor.L2Summon;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2ServitorInstance;
 import com.l2jserver.gameserver.model.entity.DimensionalRift;
 import com.l2jserver.gameserver.model.holders.ItemHolder;
@@ -726,13 +725,7 @@ public class L2Party extends AbstractPlayerGroup
 	/**
 	 * Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker.<BR>
 	 * <BR>
-	 * <B><U> Actions</U> :</B><BR>
-	 * <BR>
-	 * <li>Get the L2PcInstance owner of the L2ServitorInstance (if necessary)</li> <li>Calculate the Experience and SP reward distribution rate</li> <li>Add Experience and SP to the L2PcInstance</li><BR>
-	 * <BR>
-	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR>
-	 * <BR>
-	 * Exception are L2PetInstances that leech from the owner's XP; they get the exp indirectly, via the owner's exp gain<BR>
+	 * <B><U> Actions</U> :</B> <li>Get the L2PcInstance owner of the L2ServitorInstance (if necessary)</li> <li>Calculate the Experience and SP reward distribution rate</li> <li>Add Experience and SP to the L2PcInstance</li><BR>
 	 * @param xpReward The Experience reward to distribute
 	 * @param spReward The SP reward to distribute
 	 * @param rewardedMembers The list of L2PcInstance to reward
@@ -740,90 +733,64 @@ public class L2Party extends AbstractPlayerGroup
 	 * @param partyDmg
 	 * @param target
 	 */
-	public void distributeXpAndSp(long xpReward, int spReward, List<L2Playable> rewardedMembers, int topLvl, int partyDmg, L2Attackable target)
+	public void distributeXpAndSp(long xpReward, int spReward, List<L2PcInstance> rewardedMembers, int topLvl, int partyDmg, L2Attackable target)
 	{
-		List<L2Playable> validMembers = getValidMembers(rewardedMembers, topLvl);
-		
-		float penalty;
-		double sqLevel;
-		double preCalculation;
+		final List<L2PcInstance> validMembers = getValidMembers(rewardedMembers, topLvl);
 		
 		xpReward *= getExpBonus(validMembers.size());
 		spReward *= getSpBonus(validMembers.size());
 		
-		double sqLevelSum = 0;
-		for (L2Playable character : validMembers)
+		int sqLevelSum = 0;
+		for (L2PcInstance member : validMembers)
 		{
-			sqLevelSum += (character.getLevel() * character.getLevel());
+			sqLevelSum += (member.getLevel() * member.getLevel());
 		}
 		
 		final float vitalityPoints = (target.getVitalityPoints(partyDmg) * Config.RATE_PARTY_XP) / validMembers.size();
 		final boolean useVitalityRate = target.useVitalityRate();
 		
-		// Go through the L2PcInstances and L2PetInstances (not L2ServitorInstances) that must be rewarded
-		synchronized (rewardedMembers)
+		for (L2PcInstance member : rewardedMembers)
 		{
-			for (L2Character member : rewardedMembers)
+			if (member.isDead())
 			{
-				if (member.isDead())
-				{
-					continue;
-				}
+				continue;
+			}
+			
+			// Calculate and add the EXP and SP reward to the member
+			if (validMembers.contains(member))
+			{
+				// The servitor penalty
+				final float penalty = member.hasServitor() ? ((L2ServitorInstance) member.getSummon()).getExpPenalty() : 0;
 				
-				penalty = 0;
+				final double sqLevel = member.getLevel() * member.getLevel();
+				final double preCalculation = (sqLevel / sqLevelSum) * (1 - penalty);
 				
-				// The servitor penalty
-				if ((member.getSummon() != null) && member.getSummon().isServitor())
-				{
-					penalty = ((L2ServitorInstance) member.getSummon()).getExpPenalty();
-				}
-				// Pets that leech xp from the owner (like babypets) do not get rewarded directly
-				if (member.isPet())
-				{
-					if (((L2PetInstance) member).getPetLevelData().getOwnerExpTaken() > 0)
-					{
-						continue;
-					}
-					// TODO: This is a temporary fix while correct pet xp in party is figured out
-					penalty = (float) 0.85;
-				}
+				// Add the XP/SP points to the requested party member
+				long addexp = Math.round(member.calcStat(Stats.EXPSP_RATE, xpReward * preCalculation, null, null));
+				int addsp = (int) member.calcStat(Stats.EXPSP_RATE, spReward * preCalculation, null, null);
 				
-				// Calculate and add the EXP and SP reward to the member
-				if (validMembers.contains(member))
+				addexp = calculateExpSpPartyCutoff(member.getActingPlayer(), topLvl, addexp, addsp, useVitalityRate);
+				
+				// TODO: unhardcode me?
+				final int skillLvl = member.getActingPlayer().getSkillLevel(467);
+				if (skillLvl > 0)
 				{
-					sqLevel = member.getLevel() * member.getLevel();
-					preCalculation = (sqLevel / sqLevelSum) * (1 - penalty);
-					
-					// Add the XP/SP points to the requested party member
-					long addexp = Math.round(member.calcStat(Stats.EXPSP_RATE, xpReward * preCalculation, null, null));
-					int addsp = (int) member.calcStat(Stats.EXPSP_RATE, spReward * preCalculation, null, null);
-					if (member.isPlayer())
-					{
-						addexp = calculateExpSpPartyCutoff(member.getActingPlayer(), topLvl, addexp, addsp, useVitalityRate);
-						final int skillLvl = member.getActingPlayer().getSkillLevel(467);
-						if (skillLvl > 0)
-						{
-							final L2Skill skill = SkillTable.getInstance().getInfo(467, skillLvl);
-							if (skill.getExpNeeded() <= addexp)
-							{
-								member.getActingPlayer().absorbSoul(skill, target);
-							}
-						}
-						if (addexp > 0)
-						{
-							member.getActingPlayer().updateVitalityPoints(vitalityPoints, true, false);
-						}
-					}
-					else
+					final L2Skill skill = SkillTable.getInstance().getInfo(467, skillLvl);
+					if (skill.getExpNeeded() <= addexp)
 					{
-						member.addExpAndSp(addexp, addsp);
+						member.absorbSoul(skill, target);
 					}
 				}
-				else
+				
+				if (addexp > 0)
 				{
-					member.addExpAndSp(0, 0);
+					member.updateVitalityPoints(vitalityPoints, true, false);
 				}
 			}
+			else
+			{
+				member.addExpAndSp(0, 0);
+			}
 		}
 	}
 	
@@ -876,14 +843,14 @@ public class L2Party extends AbstractPlayerGroup
 		_partyLvl = newLevel;
 	}
 	
-	private List<L2Playable> getValidMembers(List<L2Playable> members, int topLvl)
+	private List<L2PcInstance> getValidMembers(List<L2PcInstance> members, int topLvl)
 	{
-		List<L2Playable> validMembers = new FastList<>();
+		final List<L2PcInstance> validMembers = new ArrayList<>();
 		
 		// Fixed LevelDiff cutoff point
 		if (Config.PARTY_XP_CUTOFF_METHOD.equalsIgnoreCase("level"))
 		{
-			for (L2Playable member : members)
+			for (L2PcInstance member : members)
 			{
 				if ((topLvl - member.getLevel()) <= Config.PARTY_XP_CUTOFF_LEVEL)
 				{
@@ -895,12 +862,12 @@ public class L2Party extends AbstractPlayerGroup
 		else if (Config.PARTY_XP_CUTOFF_METHOD.equalsIgnoreCase("percentage"))
 		{
 			int sqLevelSum = 0;
-			for (L2Playable member : members)
+			for (L2PcInstance member : members)
 			{
 				sqLevelSum += (member.getLevel() * member.getLevel());
 			}
 			
-			for (L2Playable member : members)
+			for (L2PcInstance member : members)
 			{
 				int sqLevel = member.getLevel() * member.getLevel();
 				if ((sqLevel * 100) >= (sqLevelSum * Config.PARTY_XP_CUTOFF_PERCENT))
@@ -913,7 +880,7 @@ public class L2Party extends AbstractPlayerGroup
 		else if (Config.PARTY_XP_CUTOFF_METHOD.equalsIgnoreCase("auto"))
 		{
 			int sqLevelSum = 0;
-			for (L2Playable member : members)
+			for (L2PcInstance member : members)
 			{
 				sqLevelSum += (member.getLevel() * member.getLevel());
 			}
@@ -928,7 +895,7 @@ public class L2Party extends AbstractPlayerGroup
 				i = BONUS_EXP_SP.length - 1;
 			}
 			
-			for (L2Playable member : members)
+			for (L2PcInstance member : members)
 			{
 				int sqLevel = member.getLevel() * member.getLevel();
 				if (sqLevel >= (sqLevelSum / (members.size() * members.size())))

+ 104 - 128
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Attackable.java

@@ -19,6 +19,7 @@
 package com.l2jserver.gameserver.model.actor;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -193,22 +194,32 @@ public class L2Attackable extends L2Npc
 	/**
 	 * This class contains all RewardInfo of the L2Attackable against the any attacker L2Character, based on amount of damage done.
 	 */
-	protected static final class RewardInfo
+	protected final class RewardInfo
 	{
 		/** The attacker L2Character concerned by this RewardInfo of this L2Attackable. */
-		protected L2Character _attacker;
+		private final L2PcInstance _attacker;
 		/** Total amount of damage done by the attacker to this L2Attackable (summon + own). */
-		protected int _dmg = 0;
+		private int _damage = 0;
 		
-		public RewardInfo(L2Character pAttacker, int pDmg)
+		public RewardInfo(L2PcInstance attacker, int damage)
 		{
-			_attacker = pAttacker;
-			_dmg = pDmg;
+			_attacker = attacker;
+			_damage = damage;
 		}
 		
-		public void addDamage(int pDmg)
+		public L2PcInstance getAttacker()
 		{
-			_dmg += pDmg;
+			return _attacker;
+		}
+		
+		public void addDamage(int damage)
+		{
+			_damage += damage;
+		}
+		
+		public int getDamage()
+		{
+			return _damage;
 		}
 		
 		@Override
@@ -589,11 +600,10 @@ public class L2Attackable extends L2Npc
 				return;
 			}
 			
-			final Map<L2Character, RewardInfo> rewards = new ConcurrentHashMap<>();
-			int damage;
-			L2Character maxDealer = null;
+			final Map<L2PcInstance, RewardInfo> rewards = new HashMap<>();
+			L2PcInstance maxDealer = null;
 			int maxDamage = 0;
-			L2Character attacker;
+			long totalDamage = 0;
 			// While Iterating over This Map Removing Object is Not Allowed
 			// Go through the _aggroList of the L2Attackable
 			for (AggroInfo info : getAggroList().values())
@@ -604,37 +614,46 @@ public class L2Attackable extends L2Npc
 				}
 				
 				// Get the L2Character corresponding to this attacker
-				attacker = info.getAttacker();
-				
-				// Get damages done by this attacker
-				damage = info.getDamage();
-				
-				// Prevent unwanted behavior
-				if (damage > 1)
+				final L2PcInstance attacker = info.getAttacker().getActingPlayer();
+				if (attacker != null)
 				{
-					// Check if damage dealer isn't too far from this (killed monster)
-					if (!Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, attacker, true))
-					{
-						continue;
-					}
+					// Get damages done by this attacker
+					final int damage = info.getDamage();
 					
-					// Calculate real damages (Summoners should get own damage plus summon's damage)
-					if (!rewards.containsKey(attacker))
-					{
-						rewards.put(attacker, new RewardInfo(attacker, damage));
-					}
-					final RewardInfo reward = rewards.get(attacker);
-					reward.addDamage(damage);
-					if ((attacker.getActingPlayer() != null) && (reward._dmg > maxDamage))
+					// Prevent unwanted behavior
+					if (damage > 1)
 					{
-						maxDealer = attacker;
-						maxDamage = reward._dmg;
+						// Check if damage dealer isn't too far from this (killed monster)
+						if (!Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, attacker, true))
+						{
+							continue;
+						}
+						
+						totalDamage += damage;
+						
+						// Calculate real damages (Summoners should get own damage plus summon's damage)
+						RewardInfo reward = rewards.get(attacker);
+						if (reward == null)
+						{
+							reward = new RewardInfo(attacker, damage);
+							rewards.put(attacker, reward);
+						}
+						else
+						{
+							reward.addDamage(damage);
+						}
+						
+						if (reward.getDamage() > maxDamage)
+						{
+							maxDealer = attacker;
+							maxDamage = reward.getDamage();
+						}
 					}
 				}
 			}
 			
 			// Manage Base, Quests and Sweep drops of the L2Attackable
-			doItemDrop((maxDealer != null) && (maxDealer.getActingPlayer() != null) && maxDealer.getActingPlayer().isOnline() ? maxDealer : lastAttacker);
+			doItemDrop((maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker);
 			
 			// Manage drop of Special Events created by GM for a defined period
 			doEventDrop(lastAttacker);
@@ -646,12 +665,6 @@ public class L2Attackable extends L2Npc
 			
 			if (!rewards.isEmpty())
 			{
-				L2Party attackerParty;
-				long exp;
-				int levelDiff, partyDmg, partyLvl, sp;
-				float partyMul, penalty;
-				RewardInfo reward2;
-				int[] tmp;
 				for (RewardInfo reward : rewards.values())
 				{
 					if (reward == null)
@@ -660,27 +673,17 @@ public class L2Attackable extends L2Npc
 					}
 					
 					// Attacker to be rewarded
-					attacker = reward._attacker;
+					final L2PcInstance attacker = reward.getAttacker();
 					
 					// Total amount of damage done
-					damage = reward._dmg;
+					final int damage = reward.getDamage();
 					
 					// Get party
-					attackerParty = attacker.getParty();
+					final L2Party attackerParty = attacker.getParty();
 					
 					// Penalty applied to the attacker's XP
-					penalty = 0;
-					// If this attacker is a player with a servitor, get Exp Penalty applied for the servitor.
-					if (attacker.isPlayer() && attacker.hasServitor())
-					{
-						penalty = ((L2ServitorInstance) attacker.getSummon()).getExpPenalty();
-					}
-					
-					// We must avoid "over damage", if any
-					if (damage > getMaxHp())
-					{
-						damage = getMaxHp();
-					}
+					// If this attacker have servitor, get Exp Penalty applied for the servitor.
+					final float penalty = attacker.hasServitor() ? ((L2ServitorInstance) attacker.getSummon()).getExpPenalty() : 0;
 					
 					// If there's NO party in progress
 					if (attackerParty == null)
@@ -692,12 +695,11 @@ public class L2Attackable extends L2Npc
 							// mob = 24, atk = 10, diff = -14 (full xp)
 							// mob = 24, atk = 28, diff = 4 (some xp)
 							// mob = 24, atk = 50, diff = 26 (no xp)
-							levelDiff = attacker.getLevel() - getLevel();
+							final int levelDiff = attacker.getLevel() - getLevel();
 							
-							tmp = calculateExpAndSp(levelDiff, damage);
-							exp = tmp[0];
-							exp *= 1 - penalty;
-							sp = tmp[1];
+							final int[] expSp = calculateExpAndSp(levelDiff, damage, totalDamage);
+							long exp = expSp[0];
+							int sp = expSp[1];
 							
 							if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
 							{
@@ -705,8 +707,10 @@ public class L2Attackable extends L2Npc
 								sp *= Config.L2JMOD_CHAMPION_REWARDS;
 							}
 							
+							exp *= 1 - penalty;
+							
 							// Check for an over-hit enabled strike
-							if (attacker.isPlayable() && isOverhit() && (attacker == getOverhitAttacker()))
+							if (isOverhit() && (getOverhitAttacker().getActingPlayer() != null) && (attacker == getOverhitAttacker().getActingPlayer()))
 							{
 								attacker.sendPacket(SystemMessageId.OVER_HIT);
 								exp += calculateOverhitExp(exp);
@@ -715,28 +719,21 @@ public class L2Attackable extends L2Npc
 							// Distribute the Exp and SP between the L2PcInstance and its L2Summon
 							if (!attacker.isDead())
 							{
-								long addexp = Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null));
-								int addsp = (int) attacker.calcStat(Stats.EXPSP_RATE, sp, null, null);
+								final long addexp = Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null));
+								final int addsp = (int) attacker.calcStat(Stats.EXPSP_RATE, sp, null, null);
 								
-								if (attacker.isPlayer())
+								final L2Skill skill = attacker.getKnownSkill(L2Skill.SKILL_SOUL_MASTERY);
+								if (skill != null)
 								{
-									final L2Skill skill = attacker.getKnownSkill(L2Skill.SKILL_SOUL_MASTERY);
-									if (skill != null)
-									{
-										if (skill.getExpNeeded() <= addexp)
-										{
-											((L2PcInstance) attacker).absorbSoul(skill, this);
-										}
-									}
-									((L2PcInstance) attacker).addExpAndSp(addexp, addsp, useVitalityRate());
-									if (addexp > 0)
+									if (skill.getExpNeeded() <= addexp)
 									{
-										((L2PcInstance) attacker).updateVitalityPoints(getVitalityPoints(damage), true, false);
+										attacker.absorbSoul(skill, this);
 									}
 								}
-								else
+								attacker.addExpAndSp(addexp, addsp, useVitalityRate());
+								if (addexp > 0)
 								{
-									attacker.addExpAndSp(addexp, addsp);
+									attacker.updateVitalityPoints(getVitalityPoints(damage), true, false);
 								}
 							}
 						}
@@ -744,33 +741,33 @@ public class L2Attackable extends L2Npc
 					else
 					{
 						// share with party members
-						partyDmg = 0;
-						partyMul = 1.f;
-						partyLvl = 0;
+						int partyDmg = 0;
+						float partyMul = 1;
+						int partyLvl = 0;
 						
 						// Get all L2Character that can be rewarded in the party
-						final List<L2Playable> rewardedMembers = new ArrayList<>();
+						final List<L2PcInstance> rewardedMembers = new ArrayList<>();
 						// Go through all L2PcInstance in the party
 						final List<L2PcInstance> groupMembers = attackerParty.isInCommandChannel() ? attackerParty.getCommandChannel().getMembers() : attackerParty.getMembers();
-						for (L2PcInstance pl : groupMembers)
+						for (L2PcInstance partyPlayer : groupMembers)
 						{
-							if ((pl == null) || pl.isDead())
+							if ((partyPlayer == null) || partyPlayer.isDead())
 							{
 								continue;
 							}
 							
 							// Get the RewardInfo of this L2PcInstance from L2Attackable rewards
-							reward2 = rewards.get(pl);
+							final RewardInfo reward2 = rewards.get(partyPlayer);
 							
 							// If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
 							if (reward2 != null)
 							{
-								if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, pl, true))
+								if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
 								{
-									partyDmg += reward2._dmg; // Add L2PcInstance damages to party damages
-									rewardedMembers.add(pl);
+									partyDmg += reward2.getDamage(); // Add L2PcInstance damages to party damages
+									rewardedMembers.add(partyPlayer);
 									
-									if (pl.getLevel() > partyLvl)
+									if (partyPlayer.getLevel() > partyLvl)
 									{
 										if (attackerParty.isInCommandChannel())
 										{
@@ -778,20 +775,20 @@ public class L2Attackable extends L2Npc
 										}
 										else
 										{
-											partyLvl = pl.getLevel();
+											partyLvl = partyPlayer.getLevel();
 										}
 									}
 								}
-								rewards.remove(pl); // Remove the L2PcInstance from the L2Attackable rewards
+								rewards.remove(partyPlayer); // Remove the L2PcInstance from the L2Attackable rewards
 							}
 							else
 							{
 								// Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded
 								// and in range of the monster.
-								if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, pl, true))
+								if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
 								{
-									rewardedMembers.add(pl);
-									if (pl.getLevel() > partyLvl)
+									rewardedMembers.add(partyPlayer);
+									if (partyPlayer.getLevel() > partyLvl)
 									{
 										if (attackerParty.isInCommandChannel())
 										{
@@ -799,49 +796,26 @@ public class L2Attackable extends L2Npc
 										}
 										else
 										{
-											partyLvl = pl.getLevel();
+											partyLvl = partyPlayer.getLevel();
 										}
 									}
 								}
 							}
-							
-							if (pl.hasPet())
-							{
-								final L2Summon summon = pl.getSummon();
-								reward2 = rewards.get(summon);
-								if (reward2 != null) // Pets are only added if they have done damage
-								{
-									if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, summon, true))
-									{
-										partyDmg += reward2._dmg; // Add summon damages to party damages
-										rewardedMembers.add(summon);
-										
-										if (summon.getLevel() > partyLvl)
-										{
-											partyLvl = summon.getLevel();
-										}
-									}
-									rewards.remove(summon); // Remove the summon from the L2Attackable rewards
-								}
-							}
 						}
 						
 						// If the party didn't killed this L2Attackable alone
-						if (partyDmg < getMaxHp())
+						if (partyDmg < totalDamage)
 						{
-							partyMul = ((float) partyDmg / (float) getMaxHp());
+							partyMul = ((float) partyDmg / totalDamage);
 						}
 						
-						// Avoid "over damage"
-						partyDmg = Math.min(partyDmg, getMaxHp());
-						
 						// Calculate the level difference between Party and L2Attackable
-						levelDiff = partyLvl - getLevel();
+						final int levelDiff = partyLvl - getLevel();
 						
 						// Calculate Exp and SP rewards
-						tmp = calculateExpAndSp(levelDiff, partyDmg);
-						exp = tmp[0];
-						sp = tmp[1];
+						final int[] expSp = calculateExpAndSp(levelDiff, partyDmg, totalDamage);
+						long exp = expSp[0];
+						int sp = expSp[1];
 						
 						if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
 						{
@@ -854,11 +828,12 @@ public class L2Attackable extends L2Npc
 						
 						// Check for an over-hit enabled strike
 						// (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
-						if (attacker.isPlayable() && isOverhit() && (attacker == getOverhitAttacker()))
+						if (isOverhit() && (getOverhitAttacker().getActingPlayer() != null) && (attacker == getOverhitAttacker().getActingPlayer()))
 						{
 							attacker.sendPacket(SystemMessageId.OVER_HIT);
 							exp += calculateOverhitExp(exp);
 						}
+						
 						// Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
 						if (partyDmg > 0)
 						{
@@ -2187,9 +2162,10 @@ public class L2Attackable extends L2Npc
 	 * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2ServitorInstance or L2Party) of the L2Attackable.
 	 * @param diff The difference of level between attacker (L2PcInstance, L2ServitorInstance or L2Party) and the L2Attackable
 	 * @param damage The damages given by the attacker (L2PcInstance, L2ServitorInstance or L2Party)
+	 * @param totalDamage The total damage done
 	 * @return
 	 */
-	private int[] calculateExpAndSp(int diff, int damage)
+	private int[] calculateExpAndSp(int diff, int damage, long totalDamage)
 	{
 		double xp;
 		double sp;
@@ -2199,13 +2175,13 @@ public class L2Attackable extends L2Npc
 			diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
 		}
 		
-		xp = ((double) getExpReward() * damage) / getMaxHp();
+		xp = ((double) getExpReward() * damage) / totalDamage;
 		if (Config.ALT_GAME_EXPONENT_XP != 0)
 		{
 			xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
 		}
 		
-		sp = ((double) getSpReward() * damage) / getMaxHp();
+		sp = ((double) getSpReward() * damage) / totalDamage;
 		if (Config.ALT_GAME_EXPONENT_SP != 0)
 		{
 			sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);