After the discussion on the forums about critical hit damage being too high, and after seeing some questionable results from various things in the game I decided to ask Josh Sawyer about how total damage was calculated in Pillars of Eternity.
His reply was
Now much of the time, I'm not seeing results in this range on weapons. So I decided to look at the source code to see how damage is actually calculated in the game. The method which I think is most relevant is below.
This is the AdjustDamageDealt method from the class CharacterStats, which is called in the AttackBase.calcDamage() method
public void AdjustDamageDealt(GameObject enemy, ref DamageInfo damage)
{
damage.DamageAmount *= this.StatDamageHealMultiplier;
if (this.OnPreDamageDealt != null)
this.OnPreDamageDealt(((Component) this).get_gameObject(), new CombatEventArgs(damage, ((Component) this).get_gameObject(), enemy));
if (this.OnAddDamage != null)
this.OnAddDamage(((Component) this).get_gameObject(), new CombatEventArgs(damage, ((Component) this).get_gameObject(), enemy));
int roll = Random.Range(1, 101);
CharacterStats enemyStats = (CharacterStats) enemy.GetComponent<CharacterStats>();
if (Object.op_Equality((Object) enemyStats, (Object) null))
return;
int toHitRollOverride = enemyStats.GetAttackerToHitRollOverride(roll);
if (this.OnAttackRollCalculated != null)
this.OnAttackRollCalculated(((Component) this).get_gameObject(), new CombatEventArgs(damage, ((Component) this).get_gameObject(), enemy));
int num1 = this.CalculateAccuracy(damage.Attack, enemy);
int num2 = enemyStats.CalculateDefense(damage.DefendedBy, damage.Attack, ((Component) this).get_gameObject());
if (damage.DefendedBy != CharacterStats.DefenseType.None)
{
this.ComputeHitAdjustment(toHitRollOverride + num1 - num2, enemyStats, ref damage);
if (damage.IsCriticalHit)
damage.DamageAmount *= this.CriticalHitMultiplier;
else if (damage.IsGraze)
damage.DamageAmount *= CharacterStats.GrazeMultiplier;
else if (damage.IsMiss)
damage.DamageAmount = 0.0f;
}
damage.AccuracyRating = num1;
damage.DefenseRating = num2;
damage.RawRoll = toHitRollOverride;
if (this.OnAdjustCritGrazeMiss != null)
this.OnAdjustCritGrazeMiss(((Component) this).get_gameObject(), new CombatEventArgs(damage, ((Component) this).get_gameObject(), enemy));
if (!damage.IsMiss)
{
if (damage.Attack.IsDisengagementAttack)
damage.DamageAmount += this.DisengagementDamageBonus;
if (damage.Attack is AttackMelee)
{
damage.DamageAmount += this.BonusMeleeDamage;
if ((damage.Attack as AttackMelee).Unarmed)
damage.DamageAmount += this.BonusUnarmedDamage;
}
for (int index = 0; index < this.BonusDamage.Length; ++index)
{
if ((double) this.BonusDamage[index] != 0.0)
{
DamagePacket.DamageProcType damageProcType = new DamagePacket.DamageProcType((DamagePacket.DamageType) index, this.BonusDamage[index]);
damage.Damage.DamageProc.Add(damageProcType);
}
}
this.AddBonusDamagePerType(damage);
this.AddBonusDamagePerRace(damage, enemyStats);
if (Object.op_Inequality((Object) damage.Attack, (Object) null))
{
Equippable equippable = (Equippable) ((Component) damage.Attack).GetComponent<Equippable>();
if (Object.op_Inequality((Object) equippable, (Object) null))
{
if (equippable is Weapon)
{
if (damage.Attack is AttackMelee)
{
damage.DamageAmount *= this.BonusMeleeWeaponDamageMult;
}
else
{
damage.DamageAmount *= this.BonusRangedWeaponDamageMult;
if (Object.op_Inequality((Object) enemy, (Object) null) && !this.IsEnemyDistant(enemy))
damage.DamageAmount *= this.BonusRangedWeaponCloseEnemyDamageMult;
}
}
equippable.ApplyItemModDamageProcs(ref damage);
}
}
}
this.ComputeInterrupt(enemyStats, ref damage);
if (this.m_isPartyMember)
{
if (Object.op_Implicit((Object) enemyStats))
{
enemyStats.RevealDefense(damage.DefendedBy);
enemyStats.RevealDT(damage.Damage.Type);
using (List<DamagePacket.DamageProcType>.Enumerator enumerator = damage.Damage.DamageProc.GetEnumerator())
{
while (enumerator.MoveNext())
{
DamagePacket.DamageProcType current = enumerator.Current;
enemyStats.RevealDT(current.Type);
}
}
}
if (damage.DefenseRating >= damage.AccuracyRating + 50)
{
GameState.AutoPause(AutoPauseOptions.PauseEvent.ExtraordinaryDefence, ((Component) this).get_gameObject(), enemy);
TutorialManager.STriggerTutorialsOfTypeFast(TutorialManager.ExclusiveTriggerType.PARTYMEM_GETS_DEFENSE_TOO_HIGH);
}
if ((double) damage.MaxDamage - (double) damage.DTRating < (double) damage.MinDamage && Object.op_Inequality((Object) ((Component) damage.Attack).GetComponent<Weapon>(), (Object) null))
GameState.AutoPause(AutoPauseOptions.PauseEvent.WeaponIneffective, ((Component) this).get_gameObject(), enemy);
}
if (this.OnPostDamageDealt == null)
return;
this.OnPostDamageDealt(((Component) this).get_gameObject(), new CombatEventArgs(damage, ((Component) this).get_gameObject(), enemy));
}
private void AddBonusDamagePerRace(DamageInfo damage, CharacterStats enemyStats)
{
if (enemyStats.CharacterRace >= (CharacterStats.Race) this.BonusDamagePerRace.Length || (double) this.BonusDamagePerRace[(int) enemyStats.CharacterRace] == 0.0)
return;
damage.DamageAmount += this.GetBonusDamagePerRace(enemyStats.CharacterRace, damage.DamageAmount);
}
Now I do not have perfect understanding of the code, and there could be connected methods that I don't know about, but what I think I'm seeing here is that damage multipliers are individually multiplying the base damage, instead of being added together in a formula like Josh presented on tumblr. I have shown this code to four other people and they agree that this looks like how it's working.
Dirty Fighting and Vicious Fighting talents to score easier critical hits
Have mostly been testing with 1H Normal style weapons, usually dual wielding
Here are some screenshots of the strange results I've been getting.
I know for a fact that there is something definitely off about spears. They seem to be doing the most damage of any of the weapons currently, and it could be a separate issue. You can create a character from scratch, equip two fine spears and use Blinding Strike on a party member or any other unit and regularly score 70 damage critical hits (before DR).
Here is a Fine Sword crit for 70.7 damage (20 Might Aumaua Rogue). There is something strange about this one because it says Medreth only has 21 Deflection :/ I'm not sure why, it should be 40-something, unless the Bliding affliction was applied before the damage hit or something.
Fine Spear crit hit for 70.8 damage (max should be 38.24)
45.3 damage with a Fine Sabre crit with Crippling Strike (18 Might Orlan Rogue)
47.9 damage sword HIT with Blinding Strike (Aumaua Rogue 20 Might)
44.8 damage sword HIT on the BB Fighter with Blinding Strike
41.9 damage War Hammer crit on Crippling Strike (Human 18 Might, max should be 38.24)
Another instance - same character of 42.4
Fine Sabre crit of 49.3 on a Crippling Strike (18 Might Human, max should be 38.24)
You get the picture.
I am not 100% sure what the problem is, I suspect some of it might be related to the way in which damage is calculated in the source code, but it's possible that some of these are separate bugs and not the same bug.
For instance, the spear screenshot is from a freshly launched game with a fresh character, no save/load or anything. Some of the others are from saved games.
Question
Sensuki
After the discussion on the forums about critical hit damage being too high, and after seeing some questionable results from various things in the game I decided to ask Josh Sawyer about how total damage was calculated in Pillars of Eternity.
His reply was
Now much of the time, I'm not seeing results in this range on weapons. So I decided to look at the source code to see how damage is actually calculated in the game. The method which I think is most relevant is below.
This is the AdjustDamageDealt method from the class CharacterStats, which is called in the AttackBase.calcDamage() method
Now I do not have perfect understanding of the code, and there could be connected methods that I don't know about, but what I think I'm seeing here is that damage multipliers are individually multiplying the base damage, instead of being added together in a formula like Josh presented on tumblr. I have shown this code to four other people and they agree that this looks like how it's working.
To test damage multiplier stacking, I am using
Here are some screenshots of the strange results I've been getting.
I know for a fact that there is something definitely off about spears. They seem to be doing the most damage of any of the weapons currently, and it could be a separate issue. You can create a character from scratch, equip two fine spears and use Blinding Strike on a party member or any other unit and regularly score 70 damage critical hits (before DR).
Here is a Fine Sword crit for 70.7 damage (20 Might Aumaua Rogue). There is something strange about this one because it says Medreth only has 21 Deflection :/ I'm not sure why, it should be 40-something, unless the Bliding affliction was applied before the damage hit or something.
Fine Spear crit hit for 70.8 damage (max should be 38.24)
45.3 damage with a Fine Sabre crit with Crippling Strike (18 Might Orlan Rogue)
47.9 damage sword HIT with Blinding Strike (Aumaua Rogue 20 Might)
44.8 damage sword HIT on the BB Fighter with Blinding Strike
41.9 damage War Hammer crit on Crippling Strike (Human 18 Might, max should be 38.24)
Another instance - same character of 42.4
Fine Sabre crit of 49.3 on a Crippling Strike (18 Might Human, max should be 38.24)
You get the picture.
Edited by SensukiI am not 100% sure what the problem is, I suspect some of it might be related to the way in which damage is calculated in the source code, but it's possible that some of these are separate bugs and not the same bug.
For instance, the spear screenshot is from a freshly launched game with a fresh character, no save/load or anything. Some of the others are from saved games.
As I learn new information, I will post it.
10 answers to this question
Recommended Posts