Modifications to the damage/armor system:

- Armor values aren't reduced from the damage taken, but instead multiply the damage (e.g. an item reduces damage by 10% instead of 10 hp). Now armor doesn't make characters invulnerable to small amounts of damage.
- Armoring isn't defined as a single "armor value", but instead there are "damage modifiers" which can be added to items and limbs. The modifiers can only affect specific types of damage and have separate multipliers for the damage amount and bleeding amount.
- Having a fire proof item on a limb doesn't make that limb invulnerable to burn damage. Item's that protect against fire now have a damage modifier for the Burn damage type.
This commit is contained in:
Joonas Rikkonen
2017-12-02 15:42:13 +02:00
parent 1d4327b690
commit 04bcc6b9b1
17 changed files with 209 additions and 148 deletions
@@ -11,24 +11,32 @@ namespace Barotrauma
Damage, Bloodloss, Pressure, Suffocation, Drowning, Burn, Husk, Disconnected
}
public enum DamageType { None, Blunt, Slash, Burn }
[Flags]
public enum DamageType
{
None = 0,
Blunt = 1,
Slash = 2,
Burn = 4,
Any = Blunt | Slash | Burn
}
struct AttackResult
{
public readonly float Damage;
public readonly float Bleeding;
public readonly bool HitArmor;
public AttackResult(float damage, float bleeding, bool hitArmor=false)
public readonly List<DamageModifier> AppliedDamageModifiers;
public AttackResult(float damage, float bleeding, List<DamageModifier> appliedDamageModifiers = null)
{
this.Damage = damage;
this.Bleeding = bleeding;
this.HitArmor = hitArmor;
this.AppliedDamageModifiers = appliedDamageModifiers;
}
}
partial class Attack
{
public readonly float Range;
@@ -0,0 +1,66 @@
using Microsoft.Xna.Framework;
using System.Xml.Linq;
namespace Barotrauma
{
class DamageModifier
{
[Serialize(DamageType.None, false)]
public DamageType DamageType
{
get;
private set;
}
[Serialize(1.0f, false)]
public float DamageMultiplier
{
get;
private set;
}
[Serialize(1.0f, false)]
public float BleedingMultiplier
{
get;
private set;
}
[Serialize("0.0,360", false)]
public Vector2 ArmorSector
{
get;
private set;
}
[Serialize(true, false)]
public bool IsArmor
{
get;
private set;
}
[Serialize(true, false)]
public bool DeflectProjectiles
{
get;
private set;
}
#if CLIENT
[Serialize(DamageSoundType.None, false)]
public DamageSoundType DamageSoundType
{
get;
private set;
}
#endif
public DamageModifier(XElement element)
{
SerializableProperty.DeserializeProperties(this, element);
ArmorSector = new Vector2(MathHelper.ToRadians(ArmorSector.X), MathHelper.ToRadians(ArmorSector.Y));
}
}
}
@@ -72,10 +72,7 @@ namespace Barotrauma
private bool isSevered;
private float severedFadeOutTimer;
private readonly Vector2 armorSector;
private readonly float armorValue;
public Vector2? MouthPos;
//a timer for delaying when a hitsound/attacksound can be played again
@@ -91,6 +88,8 @@ namespace Barotrauma
private Vector2 animTargetPos;
private float scale;
private List<DamageModifier> damageModifiers;
public float AttackTimer;
@@ -179,7 +178,7 @@ namespace Barotrauma
public float Burnt
{
get { return burnt; }
set { burnt = MathHelper.Clamp(value,0.0f,100.0f); }
protected set { burnt = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public List<WearableSprite> WearingItems
@@ -255,15 +254,7 @@ namespace Barotrauma
GameMain.World.AddJoint(pullJoint);
steerForce = element.GetAttributeFloat("steerforce", 0.0f);
//maxHealth = Math.Max(ToolBox.GetAttributeFloat(element, "health", 100.0f),1.0f);
armorSector = element.GetAttributeVector2("armorsector", Vector2.Zero);
armorSector.X = MathHelper.ToRadians(armorSector.X);
armorSector.Y = MathHelper.ToRadians(armorSector.Y);
armorValue = Math.Max(element.GetAttributeFloat("armor", 0.0f), 0.0f);
if (element.Attribute("mouthpos") != null)
{
MouthPos = ConvertUnits.ToSimUnits(element.GetAttributeVector2("mouthpos", Vector2.Zero));
@@ -272,6 +263,8 @@ namespace Barotrauma
body.BodyType = BodyType.Dynamic;
body.FarseerBody.AngularDamping = LimbAngularDamping;
damageModifiers = new List<DamageModifier>();
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -322,6 +315,9 @@ namespace Barotrauma
case "attack":
attack = new Attack(subElement);
break;
case "damagemodifier":
damageModifiers.Add(new DamageModifier(subElement));
break;
}
}
@@ -344,46 +340,51 @@ namespace Barotrauma
public AttackResult AddDamage(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound)
{
bool hitArmor = false;
float totalArmorValue = 0.0f;
List<DamageModifier> appliedDamageModifiers = new List<DamageModifier>();
if (armorValue > 0.0f && SectorHit(armorSector, position))
foreach (DamageModifier damageModifier in damageModifiers)
{
hitArmor = true;
totalArmorValue += armorValue;
if (damageModifier.DamageType.HasFlag(damageType) && SectorHit(damageModifier.ArmorSector, position))
{
appliedDamageModifiers.Add(damageModifier);
}
}
foreach (WearableSprite wearable in wearingItems)
{
if (wearable.WearableComponent.ArmorValue > 0.0f &&
SectorHit(wearable.WearableComponent.ArmorSectorLimits, position))
foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
{
hitArmor = true;
totalArmorValue += wearable.WearableComponent.ArmorValue;
if (damageModifier.DamageType.HasFlag(damageType) && SectorHit(damageModifier.ArmorSector, position))
{
appliedDamageModifiers.Add(damageModifier);
}
}
}
}
if (hitArmor)
foreach (DamageModifier damageModifier in appliedDamageModifiers)
{
totalArmorValue = Math.Max(totalArmorValue, 0.0f);
amount = Math.Max(0.0f, amount - totalArmorValue);
bleedingAmount = Math.Max(0.0f, bleedingAmount - totalArmorValue);
amount *= damageModifier.DamageMultiplier;
bleedingAmount *= damageModifier.BleedingMultiplier;
}
#if CLIENT
if (playSound)
{
DamageSoundType damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.LimbBlunt : DamageSoundType.LimbSlash;
if (hitArmor)
{
damageSoundType = DamageSoundType.LimbArmor;
}
foreach (DamageModifier damageModifier in appliedDamageModifiers)
{
if (damageModifier.DamageSoundType != DamageSoundType.None)
{
damageSoundType = damageModifier.DamageSoundType;
break;
}
}
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
}
float bloodParticleAmount = hitArmor || bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
for (int i = 0; i < bloodParticleAmount; i++)
@@ -402,9 +403,14 @@ namespace Barotrauma
#endif
if (damageType == DamageType.Burn)
{
Burnt += amount * 10.0f;
}
damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
return new AttackResult(amount, bleedingAmount, hitArmor);
return new AttackResult(amount, bleedingAmount, appliedDamageModifiers);
}
public bool SectorHit(Vector2 armorSector, Vector2 simPosition)