Why Armour doesn't work
So I was inspired to make this post by two things. First, this video:
And second: After being hit by a stone while wearing high quality armour
Basically, armour seems to have bugger all effect in the game, and I went and investigated why.
Behind the spoiler is the relevant game code and my notes about the variables.
(thanks to ZenDzee)
The inputs are:
DamageType - cut, pierce or blunt
Magnitude - how hard the weapon hits. The calculation for this one is fairly complicated and I may go over it at a later date. The damage stat on weapons in the inventory screen is a derived quantity relating to this magnitude. Basically consider it as the raw damage amount before armour comes into play.
ArmorEffectiveness - the armor value protecting the specific body part that got hit
AbsorbedDamageRatio - some kind of blanket damage reduction thing. Appears to be unused - the only entities that have it defined have it set to 1.
FactorByDamageType - Some kind of formula - altering number to change the behaviour of weapon damage types. Blunt is 1, pierce is 0.25 and cut is 0.1. As I'll go into below, this thing mostly cancels itself out and is only actually important when calculating the integer damage reduction.
Points of interest:
HitShieldOnBack - If you have a shield on your back, it adds +10 to armor if hit there.
DamageMultiplierOfBone - The damage bonus from getting in the head vs getting hit in the arm. The damage bonuses are as follows:
Head and neck: 2x pierce damage, 1.2x cut and blunt damage
Legs: 0.8x damage for all damage types.
Horse legs: 1.2 x damage for all damage types
I also found a curious bit of code that I can't understand the purpose of. It appears to make all blunt melee attacks ~ 50% stronger.
The damage calculations look a bit complicated, but with a bit of algebra they simplify nicely.
From the code:
magnitude * FactorByDamageType * 100/(100 + armour) + (1- FactorByDamageType)*(magnitude*100/(100 + armour) - 0.5* armour)
This simplifies to:
magnitude* 100/(100 + armour) - (1 - FactorByDamageType )* 0.5 * armour
For Cut damage: magnitude* 100/(100 + armour) - 0.45* armour
For Pierce damage: magnitude* 100/(100 + armour) - 0.25* armour
For Blunt damage: magnitude* 100/(100 + armour)
The formula can be divided cleanly into two parts. The first part (100/(100+armour)) is essentially a percentage reduction, and a weak one at that. With 50 armour ( a decent amount to have by the late mid game), this is a 33% reduction. With 60 armour (close to being the best you can get), it's 37.5% reduction. These figures are very low when you compare them to other games. Early ID software games, for instance, had armour ranging from 50% to 80% protection, and even then, they didn't make you feel invulnerable.
The second part is integer damage reduction. This is the main thing that protects you from cut and pierce damage. However, for blunt damage it's completely absent! This means that no matter how much armour you've cheated onto your character, you'll never be fully protected from people flinging 3 damage pebbles.
This is the core reason why armour feels ineffectual. The damage reduction for attacks in general and blunt in particular is so low as to basically not be worth having heavy armour.
How the Damage Model can be made better
A good armour damage model needs to achieve several things:
-Armour needs to feel like armour and not cardboard. High quality historical armour provided a huge amount of protection from most strikes and the gameplay needs to reflect that.
-Incidental, weak blows should do zero damage.
-A lone armoured combatant should be brought down by dozens of peasants/looters. In real life, a lone knight would be wrestled to the ground by the peasantry and knifed through eyeslits/armpits or had their armour undone before being killed. We don't have that in M&B, so it's important that such attacks from low-tier fighters still do ~1-2 damage so that lone knights who lose their horse amongst peasants can eventually be killed.
-It's important that the armour doesn't absorb too much damage from super heavy attacks - like trebuchet rocks or a lance to the face from maximum velocity.
-There should be a meaningful gameplay distinction between using Cutting, Piercing and Blunt weapons. The player should find themselves in situations where they have to weigh up the pros and cons of which item type to equip. There sort of already is a distinction between cutting and the other damage types, but ideally there should be a distinction between piercing and blunt too.
These goals can't be achieved by either an integer damage reduction or percentage absorption system.
Instead I propose a piecewise function - something that absorbs all damage from small attacks, lets some damage through for medium attacks, and allows lots of damage to get through from heavy attacks.
One way of doing this is with three joined linear functions - a flat damage reduction up to some value (let's say armour/2), a small amount of damage leakage (say ~25%) up to the armour value and full damage for higher values.
Another way is to join a curved function (such as a parabola) with a line. You just match the two up where the curve's slope becomes 1. A more aggressively curved function like a cubic or exponential could be used to further reduce the damage at low levels.
This gives more room for mixing and matching models to make pierce and blunt damage a bit more distinct. For instance, armour could be more effective at absorbing very low damage pierce attacks vs blunt attacks, but less effective at moderate damage levels.
Alternatively, blunt damage could be more effective at interrupting attacks. Currently all damage types will interrupt an attack if the damage dealt is over 5. If the threshold for blunt damage is lower, it allows blunt weapons to be more threatening to armoured opponents without necessarily having to be completely armour piercing.
So I was inspired to make this post by two things. First, this video:
And second: After being hit by a stone while wearing high quality armour
Basically, armour seems to have bugger all effect in the game, and I went and investigated why.
Behind the spoiler is the relevant game code and my notes about the variables.
Code:
public static float ComputeRawDamageNew(
DamageTypes damageType,
float magnitude,
float armorEffectiveness,
float absorbedDamageRatio)
{
float num1 = 0.0f;
float factorByDamageType = CombatStatCalculator.GetBluntDamageFactorByDamageType(damageType);
float num2 = magnitude * factorByDamageType;
float num3 = (float) (100.0 / (100.0 + (double) armorEffectiveness));
float num4 = num1 + num2 * num3;
float num5;
switch (damageType)
{
case DamageTypes.Cut:
num5 = Math.Max(0.0f, (float) ((double) magnitude * (double) num3 - (double) armorEffectiveness * 0.5));
num4 += num5 * (1f - factorByDamageType);
return num4 * absorbedDamageRatio;
case DamageTypes.Pierce:
num5 = Math.Max(0.0f, (float) ((double) magnitude * (double) num3 - (double) armorEffectiveness * 0.330000013113022));
num4 += num5 * (1f - factorByDamageType);
return num4 * absorbedDamageRatio;
case DamageTypes.Blunt:
return num4 * absorbedDamageRatio;
default:
return 0.0f;
}
}
The inputs are:
DamageType - cut, pierce or blunt
Magnitude - how hard the weapon hits. The calculation for this one is fairly complicated and I may go over it at a later date. The damage stat on weapons in the inventory screen is a derived quantity relating to this magnitude. Basically consider it as the raw damage amount before armour comes into play.
ArmorEffectiveness - the armor value protecting the specific body part that got hit
AbsorbedDamageRatio - some kind of blanket damage reduction thing. Appears to be unused - the only entities that have it defined have it set to 1.
FactorByDamageType - Some kind of formula - altering number to change the behaviour of weapon damage types. Blunt is 1, pierce is 0.25 and cut is 0.1. As I'll go into below, this thing mostly cancels itself out and is only actually important when calculating the integer damage reduction.
Code:
public static void ComputeBlowDamage(
float armorAmountFloat,
WeaponComponentData shieldOnBack,
AgentFlag victimAgentFlag,
float victimAgentAbsorbedDamageRatio,
float damageMultiplierOfBone,
float combatDifficultyMultiplier,
DamageTypes damageType,
float magnitude,
Vec3 blowPosition,
ItemObject item,
bool blockedWithShield,
bool hitShieldOnBack,
int speedBonus,
bool cancelDamage,
bool isFallDamage,
out int inflictedDamage,
out int absorbedByArmor,
out int armorAmount)
{
if (!isFallDamage)
{
int num = (int) armorAmountFloat;
armorAmount = num;
}
else
armorAmount = 0;
float armorEffectiveness = (float) armorAmount;
if (hitShieldOnBack && shieldOnBack != null)
armorEffectiveness += 10f;
float absorbedDamageRatio = victimAgentAbsorbedDamageRatio;
float rawDamage = Game.Current.BasicModels.StrikeMagnitudeModel.ComputeRawDamage(damageType, magnitude, armorEffectiveness, absorbedDamageRatio);
float num1 = 1f;
if (!blockedWithShield && !isFallDamage)
num1 = num1 * damageMultiplierOfBone * combatDifficultyMultiplier;
float num2 = rawDamage * num1;
inflictedDamage = MBMath.ClampInt((int) num2, 0, 2000);
int num3 = MBMath.ClampInt((int) ((double) Game.Current.BasicModels.StrikeMagnitudeModel.ComputeRawDamage(damageType, magnitude, 0.0f, absorbedDamageRatio) * (double) num1), 0, 2000);
absorbedByArmor = num3 - inflictedDamage;
}
Points of interest:
HitShieldOnBack - If you have a shield on your back, it adds +10 to armor if hit there.
DamageMultiplierOfBone - The damage bonus from getting in the head vs getting hit in the arm. The damage bonuses are as follows:
Head and neck: 2x pierce damage, 1.2x cut and blunt damage
Legs: 0.8x damage for all damage types.
Horse legs: 1.2 x damage for all damage types
I also found a curious bit of code that I can't understand the purpose of. It appears to make all blunt melee attacks ~ 50% stronger.
Code:
this.DamageMultiplierOfBone = victimAgent.GetDamageMultiplierForBone(attackCollisionData.CollisionBoneIndex, (DamageTypes) attackCollisionData.DamageType);
if (!attackCollisionData.IsMissile && (sbyte) attackCollisionData.DamageType == (sbyte) 1)
this.DamageMultiplierOfBone = (float) ((1.0 + (double) this.DamageMultiplierOfBone) * 0.5);
The damage calculations look a bit complicated, but with a bit of algebra they simplify nicely.
From the code:
magnitude * FactorByDamageType * 100/(100 + armour) + (1- FactorByDamageType)*(magnitude*100/(100 + armour) - 0.5* armour)
This simplifies to:
magnitude* 100/(100 + armour) - (1 - FactorByDamageType )* 0.5 * armour
For Cut damage: magnitude* 100/(100 + armour) - 0.45* armour
For Pierce damage: magnitude* 100/(100 + armour) - 0.25* armour
For Blunt damage: magnitude* 100/(100 + armour)
The formula can be divided cleanly into two parts. The first part (100/(100+armour)) is essentially a percentage reduction, and a weak one at that. With 50 armour ( a decent amount to have by the late mid game), this is a 33% reduction. With 60 armour (close to being the best you can get), it's 37.5% reduction. These figures are very low when you compare them to other games. Early ID software games, for instance, had armour ranging from 50% to 80% protection, and even then, they didn't make you feel invulnerable.
The second part is integer damage reduction. This is the main thing that protects you from cut and pierce damage. However, for blunt damage it's completely absent! This means that no matter how much armour you've cheated onto your character, you'll never be fully protected from people flinging 3 damage pebbles.
This is the core reason why armour feels ineffectual. The damage reduction for attacks in general and blunt in particular is so low as to basically not be worth having heavy armour.
How the Damage Model can be made better
A good armour damage model needs to achieve several things:
-Armour needs to feel like armour and not cardboard. High quality historical armour provided a huge amount of protection from most strikes and the gameplay needs to reflect that.
-Incidental, weak blows should do zero damage.
-A lone armoured combatant should be brought down by dozens of peasants/looters. In real life, a lone knight would be wrestled to the ground by the peasantry and knifed through eyeslits/armpits or had their armour undone before being killed. We don't have that in M&B, so it's important that such attacks from low-tier fighters still do ~1-2 damage so that lone knights who lose their horse amongst peasants can eventually be killed.
-It's important that the armour doesn't absorb too much damage from super heavy attacks - like trebuchet rocks or a lance to the face from maximum velocity.
-There should be a meaningful gameplay distinction between using Cutting, Piercing and Blunt weapons. The player should find themselves in situations where they have to weigh up the pros and cons of which item type to equip. There sort of already is a distinction between cutting and the other damage types, but ideally there should be a distinction between piercing and blunt too.
These goals can't be achieved by either an integer damage reduction or percentage absorption system.
Instead I propose a piecewise function - something that absorbs all damage from small attacks, lets some damage through for medium attacks, and allows lots of damage to get through from heavy attacks.
One way of doing this is with three joined linear functions - a flat damage reduction up to some value (let's say armour/2), a small amount of damage leakage (say ~25%) up to the armour value and full damage for higher values.
Another way is to join a curved function (such as a parabola) with a line. You just match the two up where the curve's slope becomes 1. A more aggressively curved function like a cubic or exponential could be used to further reduce the damage at low levels.
This gives more room for mixing and matching models to make pierce and blunt damage a bit more distinct. For instance, armour could be more effective at absorbing very low damage pierce attacks vs blunt attacks, but less effective at moderate damage levels.
Alternatively, blunt damage could be more effective at interrupting attacks. Currently all damage types will interrupt an attack if the damage dealt is over 5. If the threshold for blunt damage is lower, it allows blunt weapons to be more threatening to armoured opponents without necessarily having to be completely armour piercing.
Last edited by a moderator: