(bcb06cc5c) Unstable v0.9.9.0
This commit is contained in:
@@ -11,7 +11,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Projectile : ItemComponent
|
||||
partial class Projectile : ItemComponent, IServerSerializable
|
||||
{
|
||||
struct HitscanResult
|
||||
{
|
||||
@@ -32,11 +32,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public Fixture Fixture;
|
||||
public Vector2 Normal;
|
||||
public Vector2 LinearVelocity;
|
||||
|
||||
public Impact(Fixture fixture, Vector2 normal)
|
||||
public Impact(Fixture fixture, Vector2 normal, Vector2 velocity)
|
||||
{
|
||||
Fixture = fixture;
|
||||
Normal = normal;
|
||||
LinearVelocity = velocity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +49,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//a duration during which the projectile won't drop from the body it's stuck to
|
||||
private const float PersistentStickJointDuration = 1.0f;
|
||||
|
||||
private float launchImpulse;
|
||||
|
||||
private PrismaticJoint stickJoint;
|
||||
private Body stickTarget;
|
||||
|
||||
private Attack attack;
|
||||
private readonly Attack attack;
|
||||
|
||||
private Vector2 launchPos;
|
||||
|
||||
private readonly HashSet<Body> hits = new HashSet<Body>();
|
||||
|
||||
public List<Body> IgnoredBodies;
|
||||
|
||||
@@ -68,14 +70,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Body> Hits
|
||||
{
|
||||
get { return hits; }
|
||||
}
|
||||
|
||||
private float persistentStickJointTimer;
|
||||
|
||||
[Serialize(10.0f, false, description: "The impulse applied to the physics body of the item when it's launched. Higher values make the projectile faster.")]
|
||||
public float LaunchImpulse
|
||||
{
|
||||
get { return launchImpulse; }
|
||||
set { launchImpulse = value; }
|
||||
}
|
||||
public float LaunchImpulse { get; set; }
|
||||
|
||||
[Serialize(0.0f, false, description: "The rotation of the item relative to the rotation of the weapon when launched (in degrees).")]
|
||||
public float LaunchRotation
|
||||
@@ -98,6 +101,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "When set to true, the item won't fall of a target it's stuck to unless removed.")]
|
||||
public bool StickPermanently
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item stick to the character it hits.")]
|
||||
public bool StickToCharacters
|
||||
{
|
||||
@@ -136,6 +146,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How many targets the projectile can hit before it stops.")]
|
||||
public int MaxTargetsToHit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the item be deleted when it hits something.")]
|
||||
public bool RemoveOnHit
|
||||
{
|
||||
@@ -150,6 +167,17 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
public Body StickTarget
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsStuckToTarget
|
||||
{
|
||||
get { return StickTarget != null; }
|
||||
}
|
||||
|
||||
public Projectile(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
@@ -157,7 +185,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "attack") continue;
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
attack = new Attack(subElement, item.Name + ", Projectile");
|
||||
}
|
||||
}
|
||||
@@ -201,7 +229,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(launchDir * launchImpulse * item.body.Mass);
|
||||
Launch(launchDir * LaunchImpulse * item.body.Mass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +240,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void Launch(Vector2 impulse)
|
||||
{
|
||||
hits.Clear();
|
||||
MaxTargetsToHit = 2;
|
||||
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
item.AiTarget.SightRange = item.AiTarget.MaxSightRange;
|
||||
@@ -220,6 +251,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.Drop(null);
|
||||
|
||||
launchPos = item.SimPosition;
|
||||
|
||||
item.body.Enabled = true;
|
||||
item.body.ApplyLinearImpulse(impulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
|
||||
@@ -233,7 +266,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (stickJoint == null) { return; }
|
||||
|
||||
stickTarget = null;
|
||||
StickTarget = null;
|
||||
GameMain.World.Remove(stickJoint);
|
||||
stickJoint = null;
|
||||
}
|
||||
@@ -287,7 +320,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (HitscanResult h in hits)
|
||||
{
|
||||
item.body.SetTransform(h.Point, rotation);
|
||||
if (HandleProjectileCollision(h.Fixture, h.Normal))
|
||||
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
|
||||
{
|
||||
hitSomething = true;
|
||||
break;
|
||||
@@ -321,7 +354,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//ignore sensors and items
|
||||
if (fixture?.Body == null || fixture.IsSensor) { return true; }
|
||||
if (fixture.Body.UserData is Item) { return true; }
|
||||
if (fixture.Body.UserData is Item item && item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles) { return true; }
|
||||
if (fixture.Body?.UserData as string == "ruinroom") { return true; }
|
||||
|
||||
//ignore everything else than characters, sub walls and level walls
|
||||
@@ -341,7 +374,7 @@ namespace Barotrauma.Items.Components
|
||||
//ignore sensors and items
|
||||
if (fixture?.Body == null || fixture.IsSensor) { return -1; }
|
||||
|
||||
if (fixture.Body.UserData is Item item && item.GetComponent<Door>() == null) { return -1; }
|
||||
if (fixture.Body.UserData is Item item && item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles) { return -1; }
|
||||
if (fixture.Body?.UserData as string == "ruinroom") { return -1; }
|
||||
|
||||
//ignore everything else than characters, sub walls and level walls
|
||||
@@ -364,7 +397,7 @@ namespace Barotrauma.Items.Components
|
||||
while (impactQueue.Count > 0)
|
||||
{
|
||||
var impact = impactQueue.Dequeue();
|
||||
HandleProjectileCollision(impact.Fixture, impact.Normal);
|
||||
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
|
||||
}
|
||||
|
||||
if (item.body != null && item.body.FarseerBody.IsBullet)
|
||||
@@ -377,7 +410,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (stickJoint == null) { return; }
|
||||
if (stickJoint == null || StickPermanently) { return; }
|
||||
|
||||
if (persistentStickJointTimer > 0.0f)
|
||||
{
|
||||
@@ -385,19 +418,20 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f || stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f)
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
stickTarget = null;
|
||||
if (stickJoint != null)
|
||||
if (stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f ||
|
||||
stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f)
|
||||
{
|
||||
if (GameMain.World.JointList.Contains(stickJoint))
|
||||
{
|
||||
GameMain.World.Remove(stickJoint);
|
||||
}
|
||||
stickJoint = null;
|
||||
Unstick();
|
||||
}
|
||||
if (!item.body.FarseerBody.IsBullet) { IsActive = false; }
|
||||
}
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -405,6 +439,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (User != null && User.Removed) { User = null; return false; }
|
||||
if (IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (hits.Contains(target.Body)) { return false; }
|
||||
if (target.Body.UserData is Submarine sub)
|
||||
{
|
||||
Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ?
|
||||
@@ -415,9 +455,12 @@ namespace Barotrauma.Items.Components
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) - dir,
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) + dir,
|
||||
collisionCategory: Physics.CollisionWall);
|
||||
if (wallBody?.FixtureList?.First() != null && wallBody.UserData is Structure structure)
|
||||
if (wallBody?.FixtureList?.First() != null && wallBody.UserData is Structure structure &&
|
||||
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
|
||||
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
|
||||
{
|
||||
target = wallBody.FixtureList.First();
|
||||
if (hits.Contains(target.Body)) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -440,18 +483,23 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
impactQueue.Enqueue(new Impact(target, contact.Manifold.LocalNormal));
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
|
||||
return true;
|
||||
hits.Add(target.Body);
|
||||
impactQueue.Enqueue(new Impact(target, contact.Manifold.LocalNormal, item.body.LinearVelocity));
|
||||
if (hits.Count() >= MaxTargetsToHit)
|
||||
{
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleProjectileCollision(Fixture target, Vector2 collisionNormal)
|
||||
private bool HandleProjectileCollision(Fixture target, Vector2 collisionNormal, Vector2 velocity)
|
||||
{
|
||||
if (User != null && User.Removed) { User = null; }
|
||||
|
||||
if (IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
{
|
||||
@@ -472,7 +520,6 @@ namespace Barotrauma.Items.Components
|
||||
//severed limbs don't deactivate the projectile (but may still slow it down enough to make it inactive)
|
||||
if (limb.IsSevered)
|
||||
{
|
||||
target.Body.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -552,21 +599,30 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
target.Body.ApplyLinearImpulse(velocity * item.body.Mass);
|
||||
|
||||
item.body.CollisionCategories = Physics.CollisionItem;
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
|
||||
|
||||
IgnoredBodies.Clear();
|
||||
|
||||
target.Body.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass);
|
||||
if (hits.Count() >= MaxTargetsToHit)
|
||||
{
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
if (item.Prefab.DamagedByProjectiles || item.Prefab.DamagedByMeleeWeapons)
|
||||
{
|
||||
item.body.CollisionCategories = Physics.CollisionCharacter;
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform | Physics.CollisionProjectile;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.body.CollisionCategories = Physics.CollisionItem;
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
|
||||
}
|
||||
IgnoredBodies.Clear();
|
||||
}
|
||||
|
||||
if (attackResult.AppliedDamageModifiers != null &&
|
||||
attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles))
|
||||
{
|
||||
item.body.LinearVelocity *= 0.1f;
|
||||
}
|
||||
else if (Vector2.Dot(item.body.LinearVelocity, collisionNormal) < 0.0f &&
|
||||
else if (Vector2.Dot(velocity, collisionNormal) < 0.0f && hits.Count() >= MaxTargetsToHit &&
|
||||
(DoesStick ||
|
||||
(StickToCharacters && target.Body.UserData is Limb) ||
|
||||
(StickToStructures && target.Body.UserData is Structure) ||
|
||||
@@ -575,8 +631,24 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 dir = new Vector2(
|
||||
(float)Math.Cos(item.body.Rotation),
|
||||
(float)Math.Sin(item.body.Rotation));
|
||||
|
||||
StickToTarget(target.Body, dir);
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (target.Body.UserData is Structure structure && structure.Submarine != item.Submarine && structure.Submarine != null)
|
||||
{
|
||||
StickToTarget(structure.Submarine.PhysicsBody.FarseerBody, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
StickToTarget(target.Body, dir);
|
||||
}
|
||||
}
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
item.body.LinearVelocity *= 0.5f;
|
||||
|
||||
return Hitscan;
|
||||
@@ -608,7 +680,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void StickToTarget(Body targetBody, Vector2 axis)
|
||||
{
|
||||
if (stickJoint != null) return;
|
||||
if (stickJoint != null) { return; }
|
||||
|
||||
stickJoint = new PrismaticJoint(targetBody, item.body.FarseerBody, item.body.SimPosition, axis, true)
|
||||
{
|
||||
@@ -616,19 +688,38 @@ namespace Barotrauma.Items.Components
|
||||
MaxMotorForce = 30.0f,
|
||||
LimitEnabled = true
|
||||
};
|
||||
if (item.Sprite != null)
|
||||
|
||||
if (StickPermanently)
|
||||
{
|
||||
stickJoint.LowerLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * -0.3f);
|
||||
stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f);
|
||||
stickJoint.LowerLimit = stickJoint.UpperLimit = 0.0f;
|
||||
}
|
||||
else if (item.Sprite != null)
|
||||
{
|
||||
stickJoint.LowerLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * -0.3f * item.Scale);
|
||||
stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f * item.Scale);
|
||||
}
|
||||
|
||||
persistentStickJointTimer = PersistentStickJointDuration;
|
||||
stickTarget = targetBody;
|
||||
StickTarget = targetBody;
|
||||
GameMain.World.Add(stickJoint);
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
private void Unstick()
|
||||
{
|
||||
StickTarget = null;
|
||||
if (stickJoint != null)
|
||||
{
|
||||
if (GameMain.World.JointList.Contains(stickJoint))
|
||||
{
|
||||
GameMain.World.Remove(stickJoint);
|
||||
}
|
||||
stickJoint = null;
|
||||
}
|
||||
if (!item.body.FarseerBody.IsBullet) { IsActive = false; }
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
if (stickJoint != null)
|
||||
|
||||
Reference in New Issue
Block a user