Fixes to projectiles going through walls:

- Continuous collision detection (body.IsBullet) is disabled when the body is moving slow enough, not immediately when it hits something.
- The prismatic joint that sticks projectiles to walls/characters stays active for a minimum of 1 s to prevent the projectile from immediately overshooting the joint limits and falling off (usually at the wrong side of a wall/door it hit).
- Separate properties for determining which types of bodies the projectile can stick to (character/structure/item).
- Spears and syringes only stick to characters.
- Increased syringe launch impulse.

Closes #122
This commit is contained in:
Joonas Rikkonen
2017-12-14 19:25:31 +02:00
parent 1da15dbca2
commit 58e98977df
3 changed files with 68 additions and 22 deletions

View File

@@ -26,7 +26,7 @@
<Containable name="chem"/>
</ItemContainer>
<Projectile launchimpulse="10.0" doesstick="true">
<Projectile launchimpulse="20.0" sticktocharacters="true">
<Attack damage="5" bleedingdamage="0.2" structuredamage="1" damagetype="Blunt" stun="0.1" targetforce="5"/>
<StatusEffect type="OnImpact" target="Contained, Character" Condition="-50.0" disabledeltatime="true" >
<sound file="Content/Items/Medical/syringe.ogg" range="500"/>

View File

@@ -16,7 +16,7 @@
<Body width="64" height="5" density="20"/>
<Pickable slots="Any"/>
<Projectile launchimpulse="20.0" doesstick="true">
<Projectile launchimpulse="20.0" sticktocharacters="true">
<Attack damage="20" bleedingdamage="2" structuredamage="50" damagetype="Blunt" stun="0.2" targetforce="50"/>
</Projectile>
</Item>
@@ -100,7 +100,7 @@
<Body width="8" height="3" density="40"/>
<Pickable slots="Any"/>
<Projectile hitscan="true" doesstick="false" removeonhit="true" >
<Projectile hitscan="true" removeonhit="true" >
<Attack damage="40" bleedingdamage="3" structuredamage="100" damagetype="Blunt" stun="0.5" targetforce="50"/>
</Projectile>
</Item>

View File

@@ -12,9 +12,14 @@ namespace Barotrauma.Items.Components
{
class Projectile : ItemComponent
{
private float launchImpulse;
//continuous collision detection is used while the projectile is moving faster than this
const float ContinuousCollisionThreshold = 5.0f;
private bool doesStick;
//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;
@@ -24,6 +29,8 @@ namespace Barotrauma.Items.Components
public Character User;
private float persistentStickJointTimer;
[Serialize(10.0f, false)]
public float LaunchImpulse
{
@@ -39,10 +46,32 @@ namespace Barotrauma.Items.Components
}
[Serialize(false, false)]
//backwards compatibility, can stick to anything
public bool DoesStick
{
get { return doesStick; }
set { doesStick = value; }
get;
set;
}
[Serialize(false, false)]
public bool StickToCharacters
{
get;
set;
}
[Serialize(false, false)]
public bool StickToStructures
{
get;
set;
}
[Serialize(false, false)]
public bool StickToItems
{
get;
set;
}
[Serialize(false, false)]
@@ -106,7 +135,7 @@ namespace Barotrauma.Items.Components
IsActive = true;
if (stickJoint == null || !doesStick) return;
if (stickJoint == null) return;
if (stickTarget != null)
{
@@ -182,7 +211,7 @@ namespace Barotrauma.Items.Components
//the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething)
{
Item.Spawner.AddToRemoveQueue(item);
Entity.Spawner.AddToRemoveQueue(item);
}
}
@@ -190,8 +219,23 @@ namespace Barotrauma.Items.Components
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (stickJoint != null &&
(stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f || stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f))
if (item.body != null && item.body.FarseerBody.IsBullet)
{
if (item.body.LinearVelocity.LengthSquared() < ContinuousCollisionThreshold * ContinuousCollisionThreshold)
{
item.body.FarseerBody.IsBullet = false;
}
}
if (stickJoint == null) return;
if (persistentStickJointTimer > 0.0f)
{
persistentStickJointTimer -= deltaTime;
return;
}
if (stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f || stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f)
{
if (stickTarget != null)
{
@@ -218,7 +262,7 @@ namespace Barotrauma.Items.Components
stickJoint = null;
IsActive = false;
if (!item.body.FarseerBody.IsBullet) IsActive = false;
}
}
@@ -265,12 +309,9 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnUse, 1.0f, character);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character);
IsActive = false;
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
item.body.FarseerBody.IsBullet = false;
item.body.CollisionCategories = Physics.CollisionItem;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
@@ -283,17 +324,20 @@ namespace Barotrauma.Items.Components
{
item.body.LinearVelocity *= 0.1f;
}
else if (doesStick)
else if (Vector2.Dot(item.body.LinearVelocity, collisionNormal) < 0.0f &&
(DoesStick ||
(StickToCharacters && target.Body.UserData is Limb) ||
(StickToStructures && target.Body.UserData is Structure) ||
(StickToItems && target.Body.UserData is Item)))
{
Vector2 dir = new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation));
StickToTarget(target.Body, dir);
item.body.LinearVelocity *= 0.5f;
if (Vector2.Dot(item.body.LinearVelocity, collisionNormal) < 0.0f)
{
StickToTarget(target.Body, dir);
return Hitscan;
}
return Hitscan;
}
else
{
@@ -336,6 +380,8 @@ namespace Barotrauma.Items.Components
stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f);
}
persistentStickJointTimer = PersistentStickJointDuration;
item.body.FarseerBody.IgnoreCollisionWith(targetBody);
stickTarget = targetBody;
GameMain.World.AddJoint(stickJoint);