Attack configs determine whether an attack can sever limbs (instead of the amount of damage), new blood particles
This commit is contained in:
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly float Damage;
|
||||
public readonly float Bleeding;
|
||||
|
||||
|
||||
public readonly bool HitArmor;
|
||||
|
||||
public AttackResult(float damage, float bleeding, bool hitArmor=false)
|
||||
@@ -49,6 +49,8 @@ namespace Barotrauma
|
||||
|
||||
public readonly float TargetForce;
|
||||
|
||||
public readonly float SeverLimbsProbability;
|
||||
|
||||
private Sound sound;
|
||||
|
||||
private ParticleEmitterPrefab particleEmitterPrefab;
|
||||
@@ -83,32 +85,29 @@ namespace Barotrauma
|
||||
DamageType = DamageType.None;
|
||||
}
|
||||
|
||||
|
||||
damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
|
||||
damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
|
||||
structureDamage = ToolBox.GetAttributeFloat(element, "structuredamage", 0.0f);
|
||||
bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
|
||||
bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
|
||||
Stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
|
||||
|
||||
Force = ToolBox.GetAttributeFloat(element,"force", 0.0f);
|
||||
TargetForce = ToolBox.GetAttributeFloat(element, "targetforce", 0.0f);
|
||||
|
||||
Torque = ToolBox.GetAttributeFloat(element, "torque", 0.0f);
|
||||
|
||||
Stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
|
||||
SeverLimbsProbability = ToolBox.GetAttributeFloat(element, "severlimbsprobability", 0.0f);
|
||||
|
||||
Force = ToolBox.GetAttributeFloat(element,"force", 0.0f);
|
||||
TargetForce = ToolBox.GetAttributeFloat(element, "targetforce", 0.0f);
|
||||
Torque = ToolBox.GetAttributeFloat(element, "torque", 0.0f);
|
||||
|
||||
string soundPath = ToolBox.GetAttributeString(element, "sound", "");
|
||||
if (!string.IsNullOrWhiteSpace(soundPath))
|
||||
{
|
||||
sound = Sound.Load(soundPath);
|
||||
}
|
||||
|
||||
Range = ToolBox.GetAttributeFloat(element, "range", 0.0f);
|
||||
Range = ToolBox.GetAttributeFloat(element, "range", 0.0f);
|
||||
Duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f);
|
||||
|
||||
Duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f);
|
||||
|
||||
priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
|
||||
priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
|
||||
|
||||
statusEffects = new List<StatusEffect>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
|
||||
@@ -1719,64 +1719,73 @@ namespace Barotrauma
|
||||
|
||||
public virtual AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false)
|
||||
{
|
||||
var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce);
|
||||
Limb limbHit = null;
|
||||
var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce, out limbHit);
|
||||
if (limbHit == null) return new AttackResult();
|
||||
|
||||
var attackingCharacter = attacker as Character;
|
||||
if (attackingCharacter != null && attackingCharacter.AIController == null)
|
||||
{
|
||||
GameServer.Log(Name + " attacked by " + attackingCharacter.Name+". Damage: "+attackResult.Damage+" Bleeding damage: "+attackResult.Bleeding, ServerLog.MessageType.Attack);
|
||||
}
|
||||
|
||||
if (GameMain.Client == null &&
|
||||
health - attackResult.Damage <= minHealth && Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability)
|
||||
{
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (joint.CanBeSevered && (joint.LimbA == limbHit || joint.LimbB == limbHit))
|
||||
{
|
||||
AnimController.SeverLimbJoint(joint);
|
||||
|
||||
if (joint.LimbA == limbHit)
|
||||
{
|
||||
joint.LimbB.body.LinearVelocity += limbHit.LinearVelocity * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
joint.LimbA.body.LinearVelocity += limbHit.LinearVelocity * 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attackResult;
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce = 0.0f)
|
||||
{
|
||||
Limb temp = null;
|
||||
return AddDamage(worldPosition, damageType, amount, bleedingAmount, stun, playSound, attackForce, out temp);
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce, out Limb hitLimb)
|
||||
{
|
||||
hitLimb = null;
|
||||
|
||||
if (Removed) return new AttackResult();
|
||||
|
||||
SetStun(stun);
|
||||
|
||||
Limb closestLimb = null;
|
||||
|
||||
float closestDistance = 0.0f;
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
float distance = Vector2.Distance(worldPosition, limb.WorldPosition);
|
||||
if (closestLimb == null || distance < closestDistance)
|
||||
if (hitLimb == null || distance < closestDistance)
|
||||
{
|
||||
closestLimb = limb;
|
||||
hitLimb = limb;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.Abs(attackForce) > 0.0f)
|
||||
{
|
||||
closestLimb.body.ApplyForce((closestLimb.WorldPosition - worldPosition) * attackForce);
|
||||
Vector2 diff = hitLimb.WorldPosition - worldPosition;
|
||||
if (diff == Vector2.Zero) diff = Rand.Vector(1.0f);
|
||||
hitLimb.body.ApplyForce(Vector2.Normalize(diff) * attackForce, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff));
|
||||
}
|
||||
|
||||
AttackResult attackResult = closestLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound);
|
||||
|
||||
//sever joints connected to the limb if the character was killed
|
||||
//with an attack stronger than 50% of the characters max health
|
||||
//TODO: maybe add a "CanSever" option to attacks
|
||||
if (health - attackResult.Damage <= minHealth && attackResult.Damage >= MaxHealth * 0.5f)
|
||||
{
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (joint.CanBeSevered && (joint.LimbA == closestLimb || joint.LimbB == closestLimb))
|
||||
{
|
||||
AnimController.SeverLimbJoint(joint);
|
||||
|
||||
if (joint.LimbA == closestLimb)
|
||||
{
|
||||
joint.LimbB.body.LinearVelocity = closestLimb.LinearVelocity*0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
joint.LimbA.body.LinearVelocity = closestLimb.LinearVelocity * 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AttackResult attackResult = hitLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound);
|
||||
|
||||
AddDamage(damageType == DamageType.Burn ? CauseOfDeath.Burn : causeOfDeath, attackResult.Damage, null);
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace Barotrauma
|
||||
bool hitArmor = false;
|
||||
float totalArmorValue = 0.0f;
|
||||
|
||||
if (armorValue>0.0f && SectorHit(armorSector, position))
|
||||
if (armorValue > 0.0f && SectorHit(armorSector, position))
|
||||
{
|
||||
hitArmor = true;
|
||||
totalArmorValue += armorValue;
|
||||
@@ -347,8 +347,7 @@ namespace Barotrauma
|
||||
hitArmor = true;
|
||||
totalArmorValue += wearable.WearableComponent.ArmorValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hitArmor)
|
||||
{
|
||||
@@ -364,24 +363,19 @@ namespace Barotrauma
|
||||
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
|
||||
}
|
||||
|
||||
float bloodAmount = hitArmor || bleedingAmount <= 0.0f ? 0 : (int)Math.Min((int)(amount * 2.0f), 20);
|
||||
float bloodParticleAmount = hitArmor || 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 < bloodAmount; i++)
|
||||
for (int i = 0; i < bloodParticleAmount; i++)
|
||||
{
|
||||
Vector2 particleVel = WorldPosition - position;
|
||||
if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel);
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("blood",
|
||||
WorldPosition,
|
||||
particleVel * Rand.Range(100.0f, 300.0f), 0.0f, character.AnimController.CurrentHull);
|
||||
|
||||
if (i < bloodAmount / 5)
|
||||
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
|
||||
if (blood != null)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("waterblood", WorldPosition, Rand.Vector(10), 0.0f, character.AnimController.CurrentHull);
|
||||
blood.Size *= bloodParticleSize;
|
||||
}
|
||||
}
|
||||
|
||||
damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
|
||||
damage += Math.Max(amount, bleedingAmount) / character.MaxHealth * 100.0f;
|
||||
|
||||
return new AttackResult(amount, bleedingAmount, hitArmor);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
AttackResult attackResult = new AttackResult(0.0f, 0.0f);
|
||||
AttackResult attackResult = new AttackResult();
|
||||
if (attack != null)
|
||||
{
|
||||
var submarine = f2.Body.UserData as Submarine;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Barotrauma
|
||||
|
||||
if (force == 0.0f && attack.Stun == 0.0f && attack.GetDamage(1.0f) == 0.0f) return;
|
||||
|
||||
ApplyExplosionForces(worldPosition, attack.Range, force, attack.GetDamage(1.0f), attack.Stun);
|
||||
ApplyExplosionForces(worldPosition, attack, force);
|
||||
|
||||
if (flames && GameMain.Client == null)
|
||||
{
|
||||
@@ -132,9 +132,9 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public static void ApplyExplosionForces(Vector2 worldPosition, float range, float force, float damage = 0.0f, float stun = 0.0f)
|
||||
public static void ApplyExplosionForces(Vector2 worldPosition, Attack attack, float force)
|
||||
{
|
||||
if (range <= 0.0f) return;
|
||||
if (attack.Range <= 0.0f) return;
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
@@ -143,6 +143,7 @@ namespace Barotrauma
|
||||
|
||||
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
|
||||
|
||||
bool wasDead = c.IsDead;
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
|
||||
@@ -151,24 +152,36 @@ namespace Barotrauma
|
||||
//doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
|
||||
float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
|
||||
dist = Math.Max(0.0f, dist - FarseerPhysics.ConvertUnits.ToDisplayUnits(limbRadius));
|
||||
|
||||
if (dist > attack.Range) continue;
|
||||
|
||||
if (dist > range) continue;
|
||||
|
||||
float distFactor = 1.0f - dist / range;
|
||||
float distFactor = 1.0f - dist / attack.Range;
|
||||
|
||||
//solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
|
||||
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null) distFactor *= 0.1f;
|
||||
|
||||
c.AddDamage(limb.WorldPosition, DamageType.None,
|
||||
damage / c.AnimController.Limbs.Length * distFactor, 0.0f, stun * distFactor, false);
|
||||
attack.GetDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
|
||||
attack.GetBleedingDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
|
||||
attack.Stun * distFactor,
|
||||
false);
|
||||
|
||||
if (limb.WorldPosition == worldPosition) continue;
|
||||
|
||||
if (force > 0.0f)
|
||||
if (limb.WorldPosition != worldPosition && force > 0.0f)
|
||||
{
|
||||
limb.body.ApplyLinearImpulse(Vector2.Normalize(limb.WorldPosition - worldPosition) * distFactor * force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasDead && c.IsDead)
|
||||
{
|
||||
foreach (LimbJoint joint in c.AnimController.LimbJoints)
|
||||
{
|
||||
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability)
|
||||
{
|
||||
c.AnimController.SeverLimbJoint(joint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user