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
@@ -26,7 +26,7 @@
<Containable name="chem"/> <Containable name="chem"/>
</ItemContainer> </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"/> <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" > <StatusEffect type="OnImpact" target="Contained, Character" Condition="-50.0" disabledeltatime="true" >
<sound file="Content/Items/Medical/syringe.ogg" range="500"/> <sound file="Content/Items/Medical/syringe.ogg" range="500"/>
@@ -16,7 +16,7 @@
<Body width="64" height="5" density="20"/> <Body width="64" height="5" density="20"/>
<Pickable slots="Any"/> <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"/> <Attack damage="20" bleedingdamage="2" structuredamage="50" damagetype="Blunt" stun="0.2" targetforce="50"/>
</Projectile> </Projectile>
</Item> </Item>
@@ -100,7 +100,7 @@
<Body width="8" height="3" density="40"/> <Body width="8" height="3" density="40"/>
<Pickable slots="Any"/> <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"/> <Attack damage="40" bleedingdamage="3" structuredamage="100" damagetype="Blunt" stun="0.5" targetforce="50"/>
</Projectile> </Projectile>
</Item> </Item>
@@ -12,9 +12,14 @@ namespace Barotrauma.Items.Components
{ {
class Projectile : ItemComponent 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 PrismaticJoint stickJoint;
private Body stickTarget; private Body stickTarget;
@@ -24,6 +29,8 @@ namespace Barotrauma.Items.Components
public Character User; public Character User;
private float persistentStickJointTimer;
[Serialize(10.0f, false)] [Serialize(10.0f, false)]
public float LaunchImpulse public float LaunchImpulse
{ {
@@ -39,10 +46,32 @@ namespace Barotrauma.Items.Components
} }
[Serialize(false, false)] [Serialize(false, false)]
//backwards compatibility, can stick to anything
public bool DoesStick public bool DoesStick
{ {
get { return doesStick; } get;
set { doesStick = value; } 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)] [Serialize(false, false)]
@@ -106,7 +135,7 @@ namespace Barotrauma.Items.Components
IsActive = true; IsActive = true;
if (stickJoint == null || !doesStick) return; if (stickJoint == null) return;
if (stickTarget != null) 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 //the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething) if (!hitSomething)
{ {
Item.Spawner.AddToRemoveQueue(item); Entity.Spawner.AddToRemoveQueue(item);
} }
} }
@@ -190,8 +219,23 @@ namespace Barotrauma.Items.Components
{ {
ApplyStatusEffects(ActionType.OnActive, deltaTime, null); ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (stickJoint != null && if (item.body != null && item.body.FarseerBody.IsBullet)
(stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f || stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f)) {
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) if (stickTarget != null)
{ {
@@ -218,7 +262,7 @@ namespace Barotrauma.Items.Components
stickJoint = null; 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.OnUse, 1.0f, character);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character); ApplyStatusEffects(ActionType.OnImpact, 1.0f, character);
IsActive = false;
item.body.FarseerBody.OnCollision -= OnProjectileCollision; item.body.FarseerBody.OnCollision -= OnProjectileCollision;
item.body.FarseerBody.IsBullet = false;
item.body.CollisionCategories = Physics.CollisionItem; item.body.CollisionCategories = Physics.CollisionItem;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
@@ -283,17 +324,20 @@ namespace Barotrauma.Items.Components
{ {
item.body.LinearVelocity *= 0.1f; 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( Vector2 dir = new Vector2(
(float)Math.Cos(item.body.Rotation), (float)Math.Cos(item.body.Rotation),
(float)Math.Sin(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) return Hitscan;
{
StickToTarget(target.Body, dir);
return Hitscan;
}
} }
else else
{ {
@@ -336,6 +380,8 @@ namespace Barotrauma.Items.Components
stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f); stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f);
} }
persistentStickJointTimer = PersistentStickJointDuration;
item.body.FarseerBody.IgnoreCollisionWith(targetBody); item.body.FarseerBody.IgnoreCollisionWith(targetBody);
stickTarget = targetBody; stickTarget = targetBody;
GameMain.World.AddJoint(stickJoint); GameMain.World.AddJoint(stickJoint);