Fix (or a workaround) to characters teleporting inside/through colliders when leaving a sub (see #552). Gaps do a raycast out from the sub to see if there are obstacles outside, and if so, ragdolls place a collider at the corresponding position inside the sub. The collider is simply a straight axis-aligned line atm, so it doesn't work accurately with sloped ice/submarine walls, but does prevent the ragdoll from ending up inside the obstacles. TODO: use a few edges to approximate the shape of the obstacles more closely?
This commit is contained in:
@@ -100,7 +100,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
foreach (Limb limb in Limbs)
|
foreach (Limb limb in Limbs)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (limb.pullJoint != null)
|
if (limb.pullJoint != null)
|
||||||
{
|
{
|
||||||
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA);
|
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA);
|
||||||
@@ -137,6 +136,16 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outsideCollisionBlocker.Enabled && currentHull.Submarine != null)
|
||||||
|
{
|
||||||
|
var edgeShape = outsideCollisionBlocker.FixtureList[0].Shape as FarseerPhysics.Collision.Shapes.EdgeShape;
|
||||||
|
Vector2 startPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex1)) + currentHull.Submarine.Position;
|
||||||
|
Vector2 endPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex2)) + currentHull.Submarine.Position;
|
||||||
|
startPos.Y = -startPos.Y;
|
||||||
|
endPos.Y = -endPos.Y;
|
||||||
|
GUI.DrawLine(spriteBatch, startPos, endPos, Color.Gray, 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
if (character.MemState.Count > 1)
|
if (character.MemState.Count > 1)
|
||||||
{
|
{
|
||||||
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);
|
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using FarseerPhysics;
|
|||||||
using FarseerPhysics.Dynamics;
|
using FarseerPhysics.Dynamics;
|
||||||
using FarseerPhysics.Dynamics.Contacts;
|
using FarseerPhysics.Dynamics.Contacts;
|
||||||
using FarseerPhysics.Dynamics.Joints;
|
using FarseerPhysics.Dynamics.Joints;
|
||||||
|
using FarseerPhysics.Factories;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -96,6 +97,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private Category prevCollisionCategory = Category.None;
|
private Category prevCollisionCategory = Category.None;
|
||||||
|
|
||||||
|
private Body outsideCollisionBlocker;
|
||||||
|
|
||||||
public PhysicsBody Collider
|
public PhysicsBody Collider
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -356,15 +359,23 @@ namespace Barotrauma
|
|||||||
if (collider[0] == null)
|
if (collider[0] == null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!");
|
DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!");
|
||||||
collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f);
|
collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f)
|
||||||
collider[0].UserData = character;
|
{
|
||||||
collider[0].BodyType = BodyType.Dynamic;
|
UserData = character,
|
||||||
collider[0].CollisionCategories = Physics.CollisionCharacter;
|
BodyType = BodyType.Dynamic,
|
||||||
|
CollisionCategories = Physics.CollisionCharacter
|
||||||
|
};
|
||||||
collider[0].FarseerBody.AngularDamping = 5.0f;
|
collider[0].FarseerBody.AngularDamping = 5.0f;
|
||||||
collider[0].FarseerBody.FixedRotation = true;
|
collider[0].FarseerBody.FixedRotation = true;
|
||||||
collider[0].FarseerBody.OnCollision += OnLimbCollision;
|
collider[0].FarseerBody.OnCollision += OnLimbCollision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outsideCollisionBlocker = BodyFactory.CreateEdge(GameMain.World, -Vector2.UnitX * 2.0f, Vector2.UnitX * 2.0f, "blocker");
|
||||||
|
outsideCollisionBlocker.BodyType = BodyType.Static;
|
||||||
|
outsideCollisionBlocker.CollisionCategories = Physics.CollisionWall;
|
||||||
|
outsideCollisionBlocker.CollidesWith = Physics.CollisionCharacter;
|
||||||
|
outsideCollisionBlocker.Enabled = false;
|
||||||
|
|
||||||
UpdateCollisionCategories();
|
UpdateCollisionCategories();
|
||||||
|
|
||||||
foreach (var joint in LimbJoints)
|
foreach (var joint in LimbJoints)
|
||||||
@@ -492,7 +503,10 @@ namespace Barotrauma
|
|||||||
Structure structure = f2.Body.UserData as Structure;
|
Structure structure = f2.Body.UserData as Structure;
|
||||||
|
|
||||||
if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) return false;
|
if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) return false;
|
||||||
|
|
||||||
|
//only collide with the ragdoll's own blocker
|
||||||
|
if (f2.Body.UserData as string == "blocker" && f2.Body != outsideCollisionBlocker) return false;
|
||||||
|
|
||||||
//always collides with bodies other than structures
|
//always collides with bodies other than structures
|
||||||
if (structure == null)
|
if (structure == null)
|
||||||
{
|
{
|
||||||
@@ -789,15 +803,7 @@ namespace Barotrauma
|
|||||||
//in -> out
|
//in -> out
|
||||||
if (newHull == null && currentHull.Submarine != null)
|
if (newHull == null && currentHull.Submarine != null)
|
||||||
{
|
{
|
||||||
for (int i = -1; i < 2; i += 2)
|
|
||||||
{
|
|
||||||
//don't teleport outside the sub if right next to a hull
|
|
||||||
if (Hull.FindHull(findPos + new Vector2(Submarine.GridSize.X * 4.0f * i, 0.0f), currentHull) != null) return;
|
|
||||||
if (Hull.FindHull(findPos + new Vector2(0.0f, Submarine.GridSize.Y * 4.0f * i), currentHull) != null) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Gap.FindAdjacent(currentHull.ConnectedGaps, findPos, 150.0f) != null) return;
|
if (Gap.FindAdjacent(currentHull.ConnectedGaps, findPos, 150.0f) != null) return;
|
||||||
|
|
||||||
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity);
|
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity);
|
||||||
}
|
}
|
||||||
//out -> in
|
//out -> in
|
||||||
@@ -816,6 +822,48 @@ namespace Barotrauma
|
|||||||
CurrentHull = newHull;
|
CurrentHull = newHull;
|
||||||
character.Submarine = currentHull?.Submarine;
|
character.Submarine = currentHull?.Submarine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PreventOutsideCollision()
|
||||||
|
{
|
||||||
|
if (currentHull?.Submarine == null)
|
||||||
|
{
|
||||||
|
outsideCollisionBlocker.Enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectedGaps = currentHull.ConnectedGaps.Where(g => !g.IsRoomToRoom);
|
||||||
|
foreach (Gap gap in connectedGaps)
|
||||||
|
{
|
||||||
|
if (gap.IsHorizontal)
|
||||||
|
{
|
||||||
|
if (character.Position.Y > gap.Rect.Y || character.Position.Y < gap.Rect.Y - gap.Rect.Height) continue;
|
||||||
|
if (Math.Sign(gap.Rect.Center.X - currentHull.Rect.Center.X) !=
|
||||||
|
Math.Sign(character.Position.X - currentHull.Rect.Center.X))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (character.Position.X < gap.Rect.X || character.Position.X > gap.Rect.Right) continue;
|
||||||
|
if (Math.Sign((gap.Rect.Y - gap.Rect.Height / 2) - (currentHull.Rect.Center.X - currentHull.Rect.Height / 2)) !=
|
||||||
|
Math.Sign(character.Position.X - (currentHull.Rect.Center.X - currentHull.Rect.Height / 2)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gap.GetOutsideCollider(out Vector2? outsideColliderPos, out Vector2? outsideColliderNormal)) continue;
|
||||||
|
|
||||||
|
outsideCollisionBlocker.SetTransform(
|
||||||
|
outsideColliderPos.Value - currentHull.Submarine.SimPosition,
|
||||||
|
MathUtils.VectorToAngle(outsideColliderNormal.Value) - MathHelper.PiOver2);
|
||||||
|
outsideCollisionBlocker.Enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outsideCollisionBlocker.Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Teleport(Vector2 moveAmount, Vector2 velocityChange)
|
public void Teleport(Vector2 moveAmount, Vector2 velocityChange)
|
||||||
{
|
{
|
||||||
@@ -888,6 +936,7 @@ namespace Barotrauma
|
|||||||
Vector2 flowForce = Vector2.Zero;
|
Vector2 flowForce = Vector2.Zero;
|
||||||
|
|
||||||
FindHull();
|
FindHull();
|
||||||
|
PreventOutsideCollision();
|
||||||
|
|
||||||
splashSoundTimer -= deltaTime;
|
splashSoundTimer -= deltaTime;
|
||||||
|
|
||||||
@@ -1568,6 +1617,12 @@ namespace Barotrauma
|
|||||||
b.Remove();
|
b.Remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outsideCollisionBlocker != null)
|
||||||
|
{
|
||||||
|
GameMain.World.RemoveBody(outsideCollisionBlocker);
|
||||||
|
outsideCollisionBlocker = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (LimbJoints != null)
|
if (LimbJoints != null)
|
||||||
{
|
{
|
||||||
foreach (RevoluteJoint joint in LimbJoints)
|
foreach (RevoluteJoint joint in LimbJoints)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
|
using FarseerPhysics;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -13,6 +14,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static bool ShowGaps = true;
|
public static bool ShowGaps = true;
|
||||||
|
|
||||||
|
const float OutsideColliderRaycastInterval = 0.1f;
|
||||||
|
|
||||||
public readonly bool IsHorizontal;
|
public readonly bool IsHorizontal;
|
||||||
|
|
||||||
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
|
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
|
||||||
@@ -26,12 +29,18 @@ namespace Barotrauma
|
|||||||
private float lowerSurface;
|
private float lowerSurface;
|
||||||
|
|
||||||
private Vector2 lerpedFlowForce;
|
private Vector2 lerpedFlowForce;
|
||||||
|
|
||||||
//if set to true, hull connections of this gap won't be updated when changes are being done to hulls
|
//if set to true, hull connections of this gap won't be updated when changes are being done to hulls
|
||||||
public bool DisableHullRechecks;
|
public bool DisableHullRechecks;
|
||||||
|
|
||||||
//can ambient light get through the gap even if it's not open
|
//can ambient light get through the gap even if it's not open
|
||||||
public bool PassAmbientLight;
|
public bool PassAmbientLight;
|
||||||
|
|
||||||
|
//position of a collider outside the gap (for example an ice wall next to the sub)
|
||||||
|
//used by ragdolls to prevent them from ending up inside colliders when teleporting out of the sub
|
||||||
|
private Vector2? outsideColliderPos;
|
||||||
|
private Vector2? outsideColliderNormal;
|
||||||
|
private float outsideColliderRaycastTimer;
|
||||||
|
|
||||||
public float Open
|
public float Open
|
||||||
{
|
{
|
||||||
@@ -191,6 +200,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
flowForce = Vector2.Zero;
|
flowForce = Vector2.Zero;
|
||||||
|
|
||||||
|
outsideColliderRaycastTimer -= deltaTime;
|
||||||
|
|
||||||
if (open == 0.0f || linkedTo.Count == 0)
|
if (open == 0.0f || linkedTo.Count == 0)
|
||||||
{
|
{
|
||||||
lerpedFlowForce = Vector2.Zero;
|
lerpedFlowForce = Vector2.Zero;
|
||||||
@@ -505,7 +516,51 @@ namespace Barotrauma
|
|||||||
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
|
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetOutsideCollider(out Vector2? simPosition, out Vector2? normal)
|
||||||
|
{
|
||||||
|
simPosition = null;
|
||||||
|
normal = null;
|
||||||
|
|
||||||
|
if (IsRoomToRoom || Submarine == null || open <= 0.0f) return false;
|
||||||
|
|
||||||
|
if (outsideColliderRaycastTimer <= 0.0f)
|
||||||
|
{
|
||||||
|
UpdateOutsideColliderPos((Hull)linkedTo[0]);
|
||||||
|
outsideColliderRaycastTimer = OutsideColliderRaycastInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
simPosition = outsideColliderPos;
|
||||||
|
normal = outsideColliderNormal;
|
||||||
|
return simPosition != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateOutsideColliderPos(Hull hull)
|
||||||
|
{
|
||||||
|
outsideColliderNormal = null;
|
||||||
|
outsideColliderPos = null;
|
||||||
|
|
||||||
|
if (Submarine == null) return;
|
||||||
|
|
||||||
|
Vector2 rayDir;
|
||||||
|
if (IsHorizontal)
|
||||||
|
{
|
||||||
|
rayDir = new Vector2(Math.Sign(rect.Center.X - hull.Rect.Center.X), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rayDir = new Vector2(0, Math.Sign((rect.Y - rect.Height / 2) - (hull.Rect.Y - hull.Rect.Height / 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 rayStart = ConvertUnits.ToSimUnits(WorldPosition);
|
||||||
|
Vector2 rayEnd = rayStart + rayDir * 500.0f;
|
||||||
|
|
||||||
|
if (Submarine.CheckVisibility(rayStart, rayEnd) != null)
|
||||||
|
{
|
||||||
|
outsideColliderNormal = -rayDir;
|
||||||
|
outsideColliderPos = Submarine.LastPickedPosition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateOxygen()
|
private void UpdateOxygen()
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private static Vector2 lastPickedPosition;
|
private static Vector2 lastPickedPosition;
|
||||||
private static float lastPickedFraction;
|
private static float lastPickedFraction;
|
||||||
|
private static Vector2 lastPickedNormal;
|
||||||
|
|
||||||
private Md5Hash hash;
|
private Md5Hash hash;
|
||||||
|
|
||||||
@@ -120,6 +121,11 @@ namespace Barotrauma
|
|||||||
get { return lastPickedFraction; }
|
get { return lastPickedFraction; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector2 LastPickedNormal
|
||||||
|
{
|
||||||
|
get { return lastPickedNormal; }
|
||||||
|
}
|
||||||
|
|
||||||
public bool Loading
|
public bool Loading
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -593,6 +599,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
float closestFraction = 1.0f;
|
float closestFraction = 1.0f;
|
||||||
|
Vector2 closestNormal = Vector2.Zero;
|
||||||
Body closestBody = null;
|
Body closestBody = null;
|
||||||
GameMain.World.RayCast((fixture, point, normal, fraction) =>
|
GameMain.World.RayCast((fixture, point, normal, fraction) =>
|
||||||
{
|
{
|
||||||
@@ -616,6 +623,7 @@ namespace Barotrauma
|
|||||||
if (fraction < closestFraction)
|
if (fraction < closestFraction)
|
||||||
{
|
{
|
||||||
closestFraction = fraction;
|
closestFraction = fraction;
|
||||||
|
closestNormal = normal;
|
||||||
if (fixture.Body != null) closestBody = fixture.Body;
|
if (fixture.Body != null) closestBody = fixture.Body;
|
||||||
}
|
}
|
||||||
return fraction;
|
return fraction;
|
||||||
@@ -624,6 +632,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
|
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
|
||||||
lastPickedFraction = closestFraction;
|
lastPickedFraction = closestFraction;
|
||||||
|
lastPickedNormal = closestNormal;
|
||||||
|
|
||||||
return closestBody;
|
return closestBody;
|
||||||
}
|
}
|
||||||
@@ -636,6 +645,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Body closestBody = null;
|
Body closestBody = null;
|
||||||
float closestFraction = 1.0f;
|
float closestFraction = 1.0f;
|
||||||
|
Vector2 closestNormal = Vector2.Zero;
|
||||||
|
|
||||||
if (Vector2.Distance(rayStart, rayEnd) < 0.01f)
|
if (Vector2.Distance(rayStart, rayEnd) < 0.01f)
|
||||||
{
|
{
|
||||||
@@ -664,6 +674,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
closestBody = fixture.Body;
|
closestBody = fixture.Body;
|
||||||
closestFraction = fraction;
|
closestFraction = fraction;
|
||||||
|
closestNormal = normal;
|
||||||
}
|
}
|
||||||
return closestFraction;
|
return closestFraction;
|
||||||
}
|
}
|
||||||
@@ -672,6 +683,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
|
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
|
||||||
lastPickedFraction = closestFraction;
|
lastPickedFraction = closestFraction;
|
||||||
|
lastPickedNormal = closestNormal;
|
||||||
return closestBody;
|
return closestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user