diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index 6b9d32c9e..f5d90be8b 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -100,7 +100,6 @@ namespace Barotrauma foreach (Limb limb in Limbs) { - if (limb.pullJoint != null) { 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) { Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index fb5ed6df7..ccd2c9b16 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -3,6 +3,7 @@ using FarseerPhysics; using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Joints; +using FarseerPhysics.Factories; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -96,6 +97,8 @@ namespace Barotrauma private Category prevCollisionCategory = Category.None; + private Body outsideCollisionBlocker; + public PhysicsBody Collider { get @@ -356,15 +359,23 @@ namespace Barotrauma if (collider[0] == null) { DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!"); - collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f); - collider[0].UserData = character; - collider[0].BodyType = BodyType.Dynamic; - collider[0].CollisionCategories = Physics.CollisionCharacter; + collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f) + { + UserData = character, + BodyType = BodyType.Dynamic, + CollisionCategories = Physics.CollisionCharacter + }; collider[0].FarseerBody.AngularDamping = 5.0f; collider[0].FarseerBody.FixedRotation = true; 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(); foreach (var joint in LimbJoints) @@ -492,7 +503,10 @@ namespace Barotrauma Structure structure = f2.Body.UserData as Structure; 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 if (structure == null) { @@ -789,15 +803,7 @@ namespace Barotrauma //in -> out 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; - Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity); } //out -> in @@ -816,6 +822,48 @@ namespace Barotrauma CurrentHull = newHull; 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) { @@ -888,6 +936,7 @@ namespace Barotrauma Vector2 flowForce = Vector2.Zero; FindHull(); + PreventOutsideCollision(); splashSoundTimer -= deltaTime; @@ -1568,6 +1617,12 @@ namespace Barotrauma b.Remove(); } + if (outsideCollisionBlocker != null) + { + GameMain.World.RemoveBody(outsideCollisionBlocker); + outsideCollisionBlocker = null; + } + if (LimbJoints != null) { foreach (RevoluteJoint joint in LimbJoints) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Gap.cs b/Barotrauma/BarotraumaShared/Source/Map/Gap.cs index cea75437a..dc55ee578 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Gap.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Gap.cs @@ -1,4 +1,5 @@ using Barotrauma.Items.Components; +using FarseerPhysics; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -13,6 +14,8 @@ namespace Barotrauma public static bool ShowGaps = true; + const float OutsideColliderRaycastInterval = 0.1f; + public readonly bool IsHorizontal; //a value between 0.0f-1.0f (0.0 = closed, 1.0f = open) @@ -26,12 +29,18 @@ namespace Barotrauma private float lowerSurface; private Vector2 lerpedFlowForce; - + //if set to true, hull connections of this gap won't be updated when changes are being done to hulls public bool DisableHullRechecks; //can ambient light get through the gap even if it's not open 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 { @@ -191,6 +200,8 @@ namespace Barotrauma { flowForce = Vector2.Zero; + outsideColliderRaycastTimer -= deltaTime; + if (open == 0.0f || linkedTo.Count == 0) { lerpedFlowForce = Vector2.Zero; @@ -505,7 +516,51 @@ namespace Barotrauma 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() diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index ddba249ca..40ee9d171 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -75,6 +75,7 @@ namespace Barotrauma private static Vector2 lastPickedPosition; private static float lastPickedFraction; + private static Vector2 lastPickedNormal; private Md5Hash hash; @@ -120,6 +121,11 @@ namespace Barotrauma get { return lastPickedFraction; } } + public static Vector2 LastPickedNormal + { + get { return lastPickedNormal; } + } + public bool Loading { get; @@ -593,6 +599,7 @@ namespace Barotrauma } float closestFraction = 1.0f; + Vector2 closestNormal = Vector2.Zero; Body closestBody = null; GameMain.World.RayCast((fixture, point, normal, fraction) => { @@ -616,6 +623,7 @@ namespace Barotrauma if (fraction < closestFraction) { closestFraction = fraction; + closestNormal = normal; if (fixture.Body != null) closestBody = fixture.Body; } return fraction; @@ -624,6 +632,7 @@ namespace Barotrauma lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; lastPickedFraction = closestFraction; + lastPickedNormal = closestNormal; return closestBody; } @@ -636,6 +645,7 @@ namespace Barotrauma { Body closestBody = null; float closestFraction = 1.0f; + Vector2 closestNormal = Vector2.Zero; if (Vector2.Distance(rayStart, rayEnd) < 0.01f) { @@ -664,6 +674,7 @@ namespace Barotrauma { closestBody = fixture.Body; closestFraction = fraction; + closestNormal = normal; } return closestFraction; } @@ -672,6 +683,7 @@ namespace Barotrauma lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; lastPickedFraction = closestFraction; + lastPickedNormal = closestNormal; return closestBody; }