38f1ddb...178a853: v0.8.9.1, removed content folder
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
//using Microsoft.Xna.Framework.Graphics;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -15,31 +13,89 @@ namespace Barotrauma
|
||||
{
|
||||
public enum LimbType
|
||||
{
|
||||
None, LeftHand, RightHand, LeftArm, RightArm,
|
||||
None, LeftHand, RightHand, LeftArm, RightArm, LeftForearm, RightForearm,
|
||||
LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist
|
||||
};
|
||||
|
||||
class LimbJoint : RevoluteJoint
|
||||
partial class LimbJoint : RevoluteJoint
|
||||
{
|
||||
public bool IsSevered;
|
||||
public bool CanBeSevered;
|
||||
|
||||
public bool CanBeSevered => jointParams.CanBeSevered;
|
||||
public readonly JointParams jointParams;
|
||||
public readonly Ragdoll ragdoll;
|
||||
public readonly Limb LimbA, LimbB;
|
||||
|
||||
public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One)
|
||||
{
|
||||
this.jointParams = jointParams;
|
||||
this.ragdoll = ragdoll;
|
||||
LoadParams();
|
||||
}
|
||||
|
||||
public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2)
|
||||
: base(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2)
|
||||
{
|
||||
CollideConnected = false;
|
||||
MotorEnabled = true;
|
||||
MaxMotorTorque = 0.25f;
|
||||
|
||||
LimbA = limbA;
|
||||
LimbB = limbB;
|
||||
}
|
||||
|
||||
public void SaveParams()
|
||||
{
|
||||
// Saving to the params is handled only in the params level.
|
||||
return;
|
||||
|
||||
jointParams.Stiffness = MaxMotorTorque;
|
||||
if (ragdoll.IsFlipped)
|
||||
{
|
||||
jointParams.Limb1Anchor = ConvertUnits.ToDisplayUnits(new Vector2(-LocalAnchorA.X, LocalAnchorA.Y) / jointParams.Ragdoll.JointScale);
|
||||
jointParams.Limb2Anchor = ConvertUnits.ToDisplayUnits(new Vector2(-LocalAnchorB.X, LocalAnchorB.Y) / jointParams.Ragdoll.JointScale);
|
||||
jointParams.UpperLimit = MathHelper.ToDegrees(-LowerLimit);
|
||||
jointParams.LowerLimit = MathHelper.ToDegrees(-UpperLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
jointParams.Limb1Anchor = ConvertUnits.ToDisplayUnits(LocalAnchorA / jointParams.Ragdoll.JointScale);
|
||||
jointParams.Limb2Anchor = ConvertUnits.ToDisplayUnits(LocalAnchorB / jointParams.Ragdoll.JointScale);
|
||||
jointParams.UpperLimit = MathHelper.ToDegrees(UpperLimit);
|
||||
jointParams.LowerLimit = MathHelper.ToDegrees(LowerLimit);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadParams()
|
||||
{
|
||||
MaxMotorTorque = jointParams.Stiffness;
|
||||
LimitEnabled = jointParams.LimitEnabled;
|
||||
if (float.IsNaN(jointParams.LowerLimit))
|
||||
{
|
||||
jointParams.LowerLimit = 0;
|
||||
}
|
||||
if (float.IsNaN(jointParams.UpperLimit))
|
||||
{
|
||||
jointParams.UpperLimit = 0;
|
||||
}
|
||||
if (ragdoll.IsFlipped)
|
||||
{
|
||||
LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-jointParams.Limb1Anchor.X, jointParams.Limb1Anchor.Y) * jointParams.Ragdoll.JointScale);
|
||||
LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-jointParams.Limb2Anchor.X, jointParams.Limb2Anchor.Y) * jointParams.Ragdoll.JointScale);
|
||||
UpperLimit = MathHelper.ToRadians(-jointParams.LowerLimit);
|
||||
LowerLimit = MathHelper.ToRadians(-jointParams.UpperLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalAnchorA = ConvertUnits.ToSimUnits(jointParams.Limb1Anchor * jointParams.Ragdoll.JointScale);
|
||||
LocalAnchorB = ConvertUnits.ToSimUnits(jointParams.Limb2Anchor * jointParams.Ragdoll.JointScale);
|
||||
UpperLimit = MathHelper.ToRadians(jointParams.UpperLimit);
|
||||
LowerLimit = MathHelper.ToRadians(jointParams.LowerLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial class Limb
|
||||
partial class Limb : ISerializableEntity
|
||||
{
|
||||
// Note: not used
|
||||
private const float LimbDensity = 15;
|
||||
private const float LimbAngularDamping = 7;
|
||||
|
||||
@@ -47,13 +103,16 @@ namespace Barotrauma
|
||||
private const float SeveredFadeOutTime = 10.0f;
|
||||
|
||||
public readonly Character character;
|
||||
|
||||
/// <summary>
|
||||
/// Note that during the limb initialization, character.AnimController returns null, whereas this field is already assigned.
|
||||
/// </summary>
|
||||
public readonly Ragdoll ragdoll;
|
||||
public readonly LimbParams limbParams;
|
||||
|
||||
//the physics body of the limb
|
||||
public PhysicsBody body;
|
||||
|
||||
protected readonly Vector2 stepOffset;
|
||||
|
||||
public Sprite sprite, damagedSprite;
|
||||
|
||||
public Vector2 StepOffset => ConvertUnits.ToSimUnits(limbParams.StepOffset) * ragdoll.RagdollParams.JointScale;
|
||||
|
||||
public bool inWater;
|
||||
|
||||
@@ -68,16 +127,19 @@ namespace Barotrauma
|
||||
|
||||
public Vector2? MouthPos;
|
||||
|
||||
//a timer for delaying when a hitsound/attacksound can be played again
|
||||
public float SoundTimer;
|
||||
public const float SoundInterval = 0.4f;
|
||||
|
||||
public readonly Attack attack;
|
||||
private List<DamageModifier> damageModifiers;
|
||||
|
||||
private Direction dir;
|
||||
|
||||
public int HealthIndex => limbParams.HealthIndex;
|
||||
public float Scale => limbParams.Ragdoll.LimbScale;
|
||||
public float AttackPriority => limbParams.AttackPriority;
|
||||
public bool DoesFlip => limbParams.Flip;
|
||||
public float SteerForce => limbParams.SteerForce;
|
||||
|
||||
public float AttackTimer;
|
||||
public Vector2 DebugTargetPos;
|
||||
public Vector2 DebugRefPos;
|
||||
|
||||
public bool IsSevered
|
||||
{
|
||||
@@ -87,13 +149,11 @@ namespace Barotrauma
|
||||
isSevered = value;
|
||||
if (!isSevered) severedFadeOutTimer = 0.0f;
|
||||
#if CLIENT
|
||||
if (isSevered) damage = 100.0f;
|
||||
if (isSevered) damageOverlayStrength = 100.0f;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoesFlip { get; private set; }
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return character.Submarine == null ? Position : Position + character.Submarine.Position; }
|
||||
@@ -114,13 +174,9 @@ namespace Barotrauma
|
||||
get { return body.Rotation; }
|
||||
}
|
||||
|
||||
public float Scale { get; private set; }
|
||||
|
||||
//where an animcontroller is trying to pull the limb, only used for debug visualization
|
||||
public Vector2 AnimTargetPos { get; private set; }
|
||||
|
||||
public float SteerForce { get; private set; }
|
||||
|
||||
public float Mass
|
||||
{
|
||||
get { return body.Mass; }
|
||||
@@ -136,16 +192,19 @@ namespace Barotrauma
|
||||
public float Dir
|
||||
{
|
||||
get { return ((dir == Direction.Left) ? -1.0f : 1.0f); }
|
||||
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
|
||||
set { dir = (value == -1.0f) ? Direction.Left : Direction.Right; }
|
||||
}
|
||||
|
||||
public int RefJointIndex { get; private set; }
|
||||
public int RefJointIndex => limbParams.RefJoint;
|
||||
|
||||
public Vector2 StepOffset
|
||||
private List<WearableSprite> wearingItems;
|
||||
public List<WearableSprite> WearingItems
|
||||
{
|
||||
get { return stepOffset; }
|
||||
get { return wearingItems; }
|
||||
}
|
||||
|
||||
public List<WearableSprite> OtherWearables { get; private set; } = new List<WearableSprite>();
|
||||
|
||||
public bool PullJointEnabled
|
||||
{
|
||||
get { return pullJoint.Enabled; }
|
||||
@@ -161,100 +220,105 @@ namespace Barotrauma
|
||||
public Vector2 PullJointWorldAnchorA
|
||||
{
|
||||
get { return pullJoint.WorldAnchorA; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value))
|
||||
{
|
||||
string errorMsg = "Attempted to set the anchor A of a limb's pull joint to an invalid value (" + value + ")\n" + Environment.StackTrace;
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchorA:InvalidValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (Vector2.DistanceSquared(SimPosition, value) > 50.0f * 50.0f)
|
||||
{
|
||||
Vector2 diff = value - SimPosition;
|
||||
string errorMsg = "Attempted to move the anchor A of a limb's pull joint extremely far from the limb (diff: " + diff +
|
||||
", limb enabled: " + body.Enabled +
|
||||
", simple physics enabled: " + character.AnimController.SimplePhysicsEnabled + ")\n"
|
||||
+ Environment.StackTrace;
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchorA:ExcessiveValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
pullJoint.WorldAnchorA = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 PullJointWorldAnchorB
|
||||
{
|
||||
get { return pullJoint.WorldAnchorB; }
|
||||
set
|
||||
{
|
||||
{
|
||||
if (!MathUtils.IsValid(value))
|
||||
{
|
||||
string errorMsg = "Attempted to set the anchor of a limb's pull joint to an invalid value (" + value + ")\n" + Environment.StackTrace;
|
||||
string errorMsg = "Attempted to set the anchor B of a limb's pull joint to an invalid value (" + value + ")\n" + Environment.StackTrace;
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchorB:InvalidValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchor:InvalidValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (Vector2.DistanceSquared(pullJoint.WorldAnchorA, value) > 50.0f * 50.0f)
|
||||
{
|
||||
Vector2 diff = value - pullJoint.WorldAnchorA;
|
||||
string errorMsg = "Attempted to move the anchor of a limb's pull joint extremely far from the limb (diff: " + diff +
|
||||
string errorMsg = "Attempted to move the anchor B of a limb's pull joint extremely far from the limb (diff: " + diff +
|
||||
", limb enabled: " + body.Enabled +
|
||||
", simple physics enabled: " + character.AnimController.SimplePhysicsEnabled + ")\n"
|
||||
+ Environment.StackTrace;
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchorB:ExcessiveValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Limb.SetPullJointAnchor:ExcessiveValue", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
pullJoint.WorldAnchorB = value;
|
||||
}
|
||||
}
|
||||
public List<WearableSprite> WearingItems { get; private set; }
|
||||
|
||||
public Limb (Character character, XElement element, float scale = 1.0f)
|
||||
public Vector2 PullJointLocalAnchorA
|
||||
{
|
||||
get { return pullJoint.LocalAnchorA; }
|
||||
}
|
||||
|
||||
public string Name => limbParams.Name;
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Limb(Ragdoll ragdoll, Character character, LimbParams limbParams)
|
||||
{
|
||||
this.ragdoll = ragdoll;
|
||||
this.character = character;
|
||||
|
||||
WearingItems = new List<WearableSprite>();
|
||||
|
||||
this.limbParams = limbParams;
|
||||
wearingItems = new List<WearableSprite>();
|
||||
dir = Direction.Right;
|
||||
DoesFlip = element.GetAttributeBool("flip", false);
|
||||
|
||||
Scale = scale;
|
||||
|
||||
body = new PhysicsBody(element, scale);
|
||||
|
||||
if (element.GetAttributeBool("ignorecollisions", false))
|
||||
body = new PhysicsBody(limbParams);
|
||||
type = limbParams.Type;
|
||||
if (limbParams.IgnoreCollisions)
|
||||
{
|
||||
body.CollisionCategories = Category.None;
|
||||
body.CollidesWith = Category.None;
|
||||
|
||||
ignoreCollisions = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//limbs don't collide with each other
|
||||
body.CollisionCategories = Physics.CollisionCharacter;
|
||||
body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionItem;
|
||||
body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionItem & ~Physics.CollisionItemBlocking;
|
||||
}
|
||||
|
||||
body.UserData = this;
|
||||
|
||||
RefJointIndex = -1;
|
||||
|
||||
Vector2 pullJointPos = Vector2.Zero;
|
||||
|
||||
if (element.Attribute("type") != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
type = (LimbType)Enum.Parse(typeof(LimbType), element.Attribute("type").Value, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
type = LimbType.None;
|
||||
DebugConsole.ThrowError("Error in "+element+"! \""+element.Attribute("type").Value+"\" is not a valid limb type");
|
||||
}
|
||||
|
||||
|
||||
pullJointPos = element.GetAttributeVector2("pullpos", Vector2.Zero) * scale;
|
||||
pullJointPos = ConvertUnits.ToSimUnits(pullJointPos);
|
||||
|
||||
stepOffset = element.GetAttributeVector2("stepoffset", Vector2.Zero) * scale;
|
||||
stepOffset = ConvertUnits.ToSimUnits(stepOffset);
|
||||
|
||||
RefJointIndex = element.GetAttributeInt("refjoint", -1);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
type = LimbType.None;
|
||||
}
|
||||
|
||||
pullJoint = new FixedMouseJoint(body.FarseerBody, pullJointPos)
|
||||
pullJoint = new FixedMouseJoint(body.FarseerBody, ConvertUnits.ToSimUnits(limbParams.PullPos * Scale))
|
||||
{
|
||||
Enabled = false,
|
||||
MaxForce = ((type == LimbType.LeftHand || type == LimbType.RightHand) ? 400.0f : 150.0f) * body.Mass
|
||||
@@ -262,8 +326,7 @@ namespace Barotrauma
|
||||
|
||||
GameMain.World.AddJoint(pullJoint);
|
||||
|
||||
SteerForce = element.GetAttributeFloat("steerforce", 0.0f);
|
||||
|
||||
var element = limbParams.Element;
|
||||
if (element.Attribute("mouthpos") != null)
|
||||
{
|
||||
MouthPos = ConvertUnits.ToSimUnits(element.GetAttributeVector2("mouthpos", Vector2.Zero));
|
||||
@@ -278,58 +341,33 @@ namespace Barotrauma
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
string spritePath = subElement.Attribute("texture").Value;
|
||||
|
||||
string spritePathWithTags = spritePath;
|
||||
|
||||
if (character.Info != null)
|
||||
{
|
||||
spritePath = spritePath.Replace("[GENDER]", (character.Info.Gender == Gender.Female) ? "f" : "");
|
||||
spritePath = spritePath.Replace("[HEADID]", character.Info.HeadSpriteId.ToString());
|
||||
|
||||
if (character.Info.HeadSprite != null && character.Info.SpriteTags.Any())
|
||||
{
|
||||
string tags = "";
|
||||
character.Info.SpriteTags.ForEach(tag => tags += "[" + tag + "]");
|
||||
|
||||
spritePathWithTags = Path.Combine(
|
||||
Path.GetDirectoryName(spritePath),
|
||||
Path.GetFileNameWithoutExtension(spritePath) + tags + Path.GetExtension(spritePath));
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(spritePathWithTags))
|
||||
{
|
||||
sprite = new Sprite(subElement, "", spritePathWithTags);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
sprite = new Sprite(subElement, "", spritePath);
|
||||
}
|
||||
|
||||
break;
|
||||
case "damagedsprite":
|
||||
string damagedSpritePath = subElement.Attribute("texture").Value;
|
||||
|
||||
if (character.Info != null)
|
||||
{
|
||||
damagedSpritePath = damagedSpritePath.Replace("[GENDER]", (character.Info.Gender == Gender.Female) ? "f" : "");
|
||||
damagedSpritePath = damagedSpritePath.Replace("[HEADID]", character.Info.HeadSpriteId.ToString());
|
||||
}
|
||||
|
||||
damagedSprite = new Sprite(subElement, "", damagedSpritePath);
|
||||
break;
|
||||
case "attack":
|
||||
attack = new Attack(subElement);
|
||||
attack = new Attack(subElement, (character == null ? "null" : character.Name) + ", limb " + type);
|
||||
if (attack.DamageRange <= 0)
|
||||
{
|
||||
switch (body.BodyShape)
|
||||
{
|
||||
case PhysicsBody.Shape.Circle:
|
||||
attack.DamageRange = body.radius;
|
||||
break;
|
||||
case PhysicsBody.Shape.Capsule:
|
||||
attack.DamageRange = body.height / 2 + body.radius;
|
||||
break;
|
||||
case PhysicsBody.Shape.Rectangle:
|
||||
attack.DamageRange = new Vector2(body.width / 2.0f, body.height / 2.0f).Length();
|
||||
break;
|
||||
}
|
||||
attack.DamageRange = ConvertUnits.ToDisplayUnits(attack.DamageRange);
|
||||
}
|
||||
break;
|
||||
case "damagemodifier":
|
||||
damageModifiers.Add(new DamageModifier(subElement));
|
||||
damageModifiers.Add(new DamageModifier(subElement, character.Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SerializableProperties = SerializableProperty.GetProperties(this);
|
||||
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
partial void InitProjSpecific(XElement element);
|
||||
@@ -352,43 +390,53 @@ namespace Barotrauma
|
||||
pullJoint.LocalAnchorA = new Vector2(-pullJoint.LocalAnchorA.X, pullJoint.LocalAnchorA.Y);
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound)
|
||||
public AttackResult AddDamage(Vector2 position, float damage, float bleedingDamage, float burnDamage, bool playSound)
|
||||
{
|
||||
List<Affliction> afflictions = new List<Affliction>();
|
||||
if (damage > 0.0f) afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(damage));
|
||||
if (bleedingDamage > 0.0f) afflictions.Add(AfflictionPrefab.Bleeding.Instantiate(bleedingDamage));
|
||||
if (burnDamage > 0.0f) afflictions.Add(AfflictionPrefab.Burn.Instantiate(burnDamage));
|
||||
|
||||
return AddDamage(position, afflictions, playSound);
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 position, List<Affliction> afflictions, bool playSound)
|
||||
{
|
||||
List<DamageModifier> appliedDamageModifiers = new List<DamageModifier>();
|
||||
|
||||
foreach (DamageModifier damageModifier in damageModifiers)
|
||||
//create a copy of the original affliction list to prevent modifying the afflictions of an Attack/StatusEffect etc
|
||||
afflictions = new List<Affliction>(afflictions);
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
if (damageModifier.DamageType == DamageType.None) continue;
|
||||
if (damageModifier.DamageType.HasFlag(damageType) && SectorHit(damageModifier.ArmorSector, position))
|
||||
foreach (DamageModifier damageModifier in damageModifiers)
|
||||
{
|
||||
appliedDamageModifiers.Add(damageModifier);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (WearableSprite wearable in WearingItems)
|
||||
{
|
||||
foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
|
||||
{
|
||||
if (damageModifier.DamageType == DamageType.None) continue;
|
||||
if (damageModifier.DamageType.HasFlag(damageType) && SectorHit(damageModifier.ArmorSector, position))
|
||||
if (!damageModifier.MatchesAffliction(afflictions[i])) continue;
|
||||
if (SectorHit(damageModifier.ArmorSector, position))
|
||||
{
|
||||
afflictions[i] = afflictions[i].CreateMultiplied(damageModifier.DamageMultiplier);
|
||||
appliedDamageModifiers.Add(damageModifier);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (WearableSprite wearable in wearingItems)
|
||||
{
|
||||
foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
|
||||
{
|
||||
if (!damageModifier.MatchesAffliction(afflictions[i])) continue;
|
||||
if (SectorHit(damageModifier.ArmorSector, position))
|
||||
{
|
||||
afflictions[i] = afflictions[i].CreateMultiplied(damageModifier.DamageMultiplier);
|
||||
appliedDamageModifiers.Add(damageModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DamageModifier damageModifier in appliedDamageModifiers)
|
||||
{
|
||||
amount *= damageModifier.DamageMultiplier;
|
||||
bleedingAmount *= damageModifier.BleedingMultiplier;
|
||||
}
|
||||
AddDamageProjSpecific(position, afflictions, playSound, appliedDamageModifiers);
|
||||
|
||||
AddDamageProjSpecific(position, damageType, amount, bleedingAmount, playSound, appliedDamageModifiers);
|
||||
|
||||
return new AttackResult(amount, bleedingAmount, appliedDamageModifiers);
|
||||
return new AttackResult(afflictions, this, appliedDamageModifiers);
|
||||
}
|
||||
|
||||
partial void AddDamageProjSpecific(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound, List<DamageModifier> appliedDamageModifiers);
|
||||
partial void AddDamageProjSpecific(Vector2 position, List<Affliction> afflictions, bool playSound, List<DamageModifier> appliedDamageModifiers);
|
||||
|
||||
public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
|
||||
{
|
||||
@@ -408,7 +456,7 @@ namespace Barotrauma
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
|
||||
if (LinearVelocity.X > 500.0f)
|
||||
{
|
||||
//DebugConsole.ThrowError("CHARACTER EXPLODED");
|
||||
@@ -430,22 +478,26 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (character.IsDead) return;
|
||||
|
||||
SoundTimer -= deltaTime;
|
||||
if (attack != null)
|
||||
{
|
||||
attack.UpdateCoolDown(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
|
||||
/// </summary>
|
||||
public bool UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget, out AttackResult attackResult, float distance = -1)
|
||||
{
|
||||
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
|
||||
|
||||
AttackTimer += deltaTime;
|
||||
|
||||
body.ApplyTorque(Mass * character.AnimController.Dir * attack.Torque);
|
||||
attackResult = default(AttackResult);
|
||||
float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
|
||||
bool wasRunning = attack.IsRunning;
|
||||
attack.UpdateAttackTimer(deltaTime);
|
||||
|
||||
bool wasHit = false;
|
||||
Body structureBody = null;
|
||||
if (damageTarget != null)
|
||||
{
|
||||
switch (attack.HitDetectionType)
|
||||
@@ -456,15 +508,29 @@ namespace Barotrauma
|
||||
List<Body> ignoredBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
|
||||
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
|
||||
|
||||
var body = Submarine.PickBody(
|
||||
structureBody = Submarine.PickBody(
|
||||
SimPosition, attackPosition,
|
||||
ignoredBodies, Physics.CollisionWall);
|
||||
|
||||
wasHit = body == null;
|
||||
|
||||
if (damageTarget is Item && structureBody?.UserData is Item)
|
||||
{
|
||||
// If the attack is aimed to an item and hits an item, it's successful
|
||||
wasHit = true;
|
||||
}
|
||||
else if (damageTarget is Structure && structureBody?.UserData is Structure)
|
||||
{
|
||||
// If the attack is aimed to a structure and hits a structure, it's successful
|
||||
wasHit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the attack is aimed to a character but hits a structure, the hit is blocked.
|
||||
wasHit = structureBody == null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HitDetection.Contact:
|
||||
List<Body> targetBodies = new List<Body>();
|
||||
var targetBodies = new List<Body>();
|
||||
if (damageTarget is Character targetCharacter)
|
||||
{
|
||||
foreach (Limb limb in targetCharacter.AnimController.Limbs)
|
||||
@@ -498,10 +564,10 @@ namespace Barotrauma
|
||||
contactEdge.Contact.IsTouching &&
|
||||
targetBodies.Any(b => b == contactEdge.Contact.FixtureA?.Body || b == contactEdge.Contact.FixtureB?.Body))
|
||||
{
|
||||
structureBody = targetBodies.LastOrDefault();
|
||||
wasHit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
}
|
||||
@@ -511,63 +577,134 @@ namespace Barotrauma
|
||||
|
||||
if (wasHit)
|
||||
{
|
||||
if (AttackTimer >= attack.Duration && damageTarget != null)
|
||||
wasHit = damageTarget != null;
|
||||
}
|
||||
|
||||
if (wasHit)
|
||||
{
|
||||
bool playSound = false;
|
||||
#if CLIENT
|
||||
playSound = LastAttackSoundTime < Timing.TotalTime - SoundInterval;
|
||||
if (playSound)
|
||||
{
|
||||
attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, (SoundTimer <= 0.0f));
|
||||
SoundTimer = SoundInterval;
|
||||
LastAttackSoundTime = SoundInterval;
|
||||
}
|
||||
#endif
|
||||
attackResult = attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, playSound);
|
||||
if (structureBody != null && attack.StickChance > Rand.Range(0.0f, 1.0f, Rand.RandSync.Server))
|
||||
{
|
||||
// TODO: use the hit pos?
|
||||
var localFront = body.GetLocalFront(MathHelper.ToRadians(ragdoll.RagdollParams.SpritesheetOrientation));
|
||||
var from = body.FarseerBody.GetWorldPoint(localFront);
|
||||
var to = from;
|
||||
var drawPos = body.DrawPosition;
|
||||
StickTo(structureBody, from, to);
|
||||
}
|
||||
attack.ResetAttackTimer();
|
||||
attack.SetCoolDown();
|
||||
}
|
||||
|
||||
Vector2 diff = attackPosition - SimPosition;
|
||||
if (diff.LengthSquared() < 0.00001f) return;
|
||||
|
||||
if (attack.ApplyForceOnLimbs != null)
|
||||
bool applyForces = (!attack.ApplyForcesOnlyOnce || !wasRunning) && diff.LengthSquared() > 0.00001f;
|
||||
if (applyForces)
|
||||
{
|
||||
foreach (int limbIndex in attack.ApplyForceOnLimbs)
|
||||
body.ApplyTorque(Mass * character.AnimController.Dir * attack.Torque);
|
||||
if (attack.ForceOnLimbIndices != null && attack.ForceOnLimbIndices.Count > 0)
|
||||
{
|
||||
if (limbIndex < 0 || limbIndex >= character.AnimController.Limbs.Length) continue;
|
||||
foreach (int limbIndex in attack.ForceOnLimbIndices)
|
||||
{
|
||||
if (limbIndex < 0 || limbIndex >= character.AnimController.Limbs.Length) continue;
|
||||
|
||||
Limb limb = character.AnimController.Limbs[limbIndex];
|
||||
Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
|
||||
limb.body.ApplyLinearImpulse(
|
||||
limb.Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
|
||||
Limb limb = character.AnimController.Limbs[limbIndex];
|
||||
Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
|
||||
limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
|
||||
body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
|
||||
}
|
||||
}
|
||||
else
|
||||
return wasHit;
|
||||
}
|
||||
|
||||
private WeldJoint attachJoint;
|
||||
private WeldJoint colliderJoint;
|
||||
public bool IsStuck => attachJoint != null;
|
||||
|
||||
/// <summary>
|
||||
/// Attach the limb to a target with WeldJoints.
|
||||
/// Uses sim units.
|
||||
/// </summary>
|
||||
private void StickTo(Body target, Vector2 from, Vector2 to)
|
||||
{
|
||||
if (attachJoint != null)
|
||||
{
|
||||
Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
|
||||
body.ApplyLinearImpulse(Mass * attack.Force *
|
||||
Vector2.Normalize(attackPosition - SimPosition), forcePos);
|
||||
// Already attached to the target body, no need to do anything
|
||||
if (attachJoint.BodyB == target) { return; }
|
||||
Release();
|
||||
}
|
||||
|
||||
if (!ragdoll.IsStuck)
|
||||
{
|
||||
PhysicsBody mainLimbBody = ragdoll.MainLimb.body;
|
||||
Body colliderBody = ragdoll.Collider.FarseerBody;
|
||||
Vector2 mainLimbLocalFront = mainLimbBody.GetLocalFront(MathHelper.ToRadians(ragdoll.RagdollParams.SpritesheetOrientation));
|
||||
if (Dir < 0)
|
||||
{
|
||||
mainLimbLocalFront.X = -mainLimbLocalFront.X;
|
||||
}
|
||||
Vector2 mainLimbFront = mainLimbBody.FarseerBody.GetWorldPoint(mainLimbLocalFront);
|
||||
colliderBody.SetTransform(mainLimbBody.SimPosition, mainLimbBody.Rotation);
|
||||
// Attach the collider to the main body so that they don't go out of sync (TODO: why is the collider still rotated 90d off?)
|
||||
colliderJoint = new WeldJoint(colliderBody, mainLimbBody.FarseerBody, mainLimbFront, mainLimbFront, true)
|
||||
{
|
||||
KinematicBodyB = true,
|
||||
CollideConnected = false
|
||||
};
|
||||
GameMain.World.AddJoint(colliderJoint);
|
||||
}
|
||||
|
||||
attachJoint = new WeldJoint(body.FarseerBody, target, from, to, true)
|
||||
{
|
||||
FrequencyHz = 1,
|
||||
DampingRatio = 0.5f,
|
||||
KinematicBodyB = true,
|
||||
CollideConnected = false
|
||||
};
|
||||
GameMain.World.AddJoint(attachJoint);
|
||||
}
|
||||
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (!IsStuck) { return; }
|
||||
GameMain.World.RemoveJoint(attachJoint);
|
||||
attachJoint = null;
|
||||
if (colliderJoint != null)
|
||||
{
|
||||
GameMain.World.RemoveJoint(colliderJoint);
|
||||
colliderJoint = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
if (sprite != null)
|
||||
{
|
||||
sprite.Remove();
|
||||
sprite = null;
|
||||
}
|
||||
|
||||
if (damagedSprite != null)
|
||||
{
|
||||
damagedSprite.Remove();
|
||||
damagedSprite = null;
|
||||
}
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
body.Remove();
|
||||
body = null;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (LightSource != null)
|
||||
{
|
||||
LightSource.Remove();
|
||||
}
|
||||
#endif
|
||||
body?.Remove();
|
||||
body = null;
|
||||
Release();
|
||||
RemoveProjSpecific();
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific();
|
||||
|
||||
public void LoadParams()
|
||||
{
|
||||
attack?.Deserialize();
|
||||
pullJoint.LocalAnchorA = ConvertUnits.ToSimUnits(limbParams.PullPos * Scale);
|
||||
LoadParamsProjSpecific();
|
||||
}
|
||||
|
||||
partial void LoadParamsProjSpecific();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user