(38b5d9aad) Experimental changes to syncing ragdolled (unconscious/dead) characters: - Higher error tolerance when syncing the positions. It's often hard to get the main limb exactly to the same position as the collider, because the positions of the limbs aren't synced and the pose of the ragdoll may differ between the server and clients. Increasing the tolerance makes it less likely for dead/unconscious characters to "twitch" when the game attempts to force the main limb to the position of the collider. - If the position of the ragdoll differs from the position of the collider so much that CheckDistFromCollider disables limb collisions, apply an additional force to all limbs to force the ragdoll to the correct position. Otherwise the ragdoll can occasionally start "hanging" midair, clipping through solid objects, because the main limb's pull joint doesn't necessarily have enough force to pull the entire ragdoll up to the collider.

This commit is contained in:
Joonas Rikkonen
2019-04-01 22:47:03 +03:00
parent 11f2bf4b38
commit ec7e23061b
130 changed files with 1359 additions and 2352 deletions
@@ -28,10 +28,8 @@ namespace Barotrauma
public Explosion(float range, float force, float damage, float structureDamage, float empStrength = 0.0f)
{
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range)
{
SeverLimbsProbability = 1.0f
};
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range);
attack.SeverLimbsProbability = 1.0f;
this.force = force;
this.empStrength = empStrength;
sparks = true;
@@ -185,6 +183,9 @@ namespace Barotrauma
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
@@ -175,11 +175,12 @@ namespace Barotrauma
LimitSize();
UpdateProjSpecific(growModifier);
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
}
partial void UpdateProjSpecific(float growModifier);
@@ -292,6 +293,10 @@ namespace Barotrauma
//evaporate some of the water
hull.WaterVolume -= extinguishAmount;
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
@@ -320,11 +325,12 @@ namespace Barotrauma
size.X -= extinguishAmount;
hull.WaterVolume -= extinguishAmount;
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
}
public void Extinguish(float deltaTime, float amount, Vector2 worldPosition)
@@ -88,7 +88,6 @@ namespace Barotrauma
Gap.UpdateHulls();
}
OxygenPercentage = prevOxygenPercentage;
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
Pressure = surface;
}
@@ -231,7 +231,7 @@ namespace Barotrauma
public static Level CreateRandom(LocationConnection locationConnection)
{
string seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName;
string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name;
float sizeFactor = MathUtils.InverseLerp(
MapGenerationParams.Instance.SmallLevelConnectionLength,
@@ -1522,49 +1522,21 @@ namespace Barotrauma
outpost.MakeOutpost();
Point? minSize = null;
DockingPort subPort = null;
if (Submarine.MainSub != null)
{
Point subSize = Submarine.MainSub.GetDockedBorders().Size;
Point outpostSize = outpost.GetDockedBorders().Size;
minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y);
float closestDistance = float.MaxValue;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine != Submarine.MainSub) { continue; }
//the submarine port has to be at the top of the sub
if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; }
float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X);
if (dist < closestDistance)
{
subPort = port;
closestDistance = dist;
}
}
}
float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X;
//don't try to compensate if the port is very far from the sub's center of mass
if (Math.Abs(subDockingPortOffset) > 2000.0f)
{
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f);
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
DebugConsole.NewMessage(warningMsg, Color.Orange);
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg);
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset));
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize));
if ((i == 0) == !Mirrored)
{
StartOutpost = outpost;
if (GameMain.GameSession?.StartLocation != null) { outpost.Name = GameMain.GameSession.StartLocation.Name; }
}
else
{
EndOutpost = outpost;
if (GameMain.GameSession?.EndLocation != null) { outpost.Name = GameMain.GameSession.EndLocation.Name; }
}
}
}
@@ -15,6 +15,7 @@ namespace Barotrauma
public Vector3 Position;
public float NetworkUpdateTimer;
public const float NetworkUpdateInterval = 0.2f;
public float Scale;
@@ -343,7 +343,7 @@ namespace Barotrauma
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj });
obj.NeedsNetworkSyncing = false;
obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval;
obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval;
}
}
@@ -432,16 +432,12 @@ namespace Barotrauma
{
if (ForceFluctuationStrength > 0.0f)
{
//no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers
if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any())
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
{
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
{
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
}
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
}
}
@@ -16,8 +16,6 @@ namespace Barotrauma
public int TypeChangeTimer;
public string BaseName { get => baseName; }
public string Name { get; private set; }
public Vector2 MapPosition { get; private set; }
@@ -34,10 +32,10 @@ namespace Barotrauma
get
{
CheckMissionCompleted();
for (int i = availableMissions.Count; i < Connections.Count * 2; i++)
{
int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue;
int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue;
MTRandom rand = new MTRandom(seed);
LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count];
@@ -48,7 +46,7 @@ namespace Barotrauma
if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; }
if (GameSettings.VerboseLogging && mission != null)
{
DebugConsole.NewMessage("Generated a new mission for a location (location: " + Name + ", seed: " + seed.ToString("X") + ", missions completed: " + MissionsCompleted + ", type: " + mission.Name + ")", Color.White);
DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White);
}
availableMissions.Add(mission);
}
@@ -77,10 +75,10 @@ namespace Barotrauma
}
}
public Location(Vector2 mapPosition, int? zone, Random rand)
public Location(Vector2 mapPosition, int? zone)
{
this.Type = LocationType.Random(rand, zone);
this.Name = RandomName(Type, rand);
this.Type = LocationType.Random("", zone);
this.Name = RandomName(Type);
this.MapPosition = mapPosition;
PortraitId = ToolBox.StringToInt(Name);
@@ -88,9 +86,9 @@ namespace Barotrauma
Connections = new List<LocationConnection>();
}
public static Location CreateRandom(Vector2 position, int? zone , Random rand)
public static Location CreateRandom(Vector2 position, int? zone)
{
return new Location(position, zone, rand);
return new Location(position, zone);
}
public IEnumerable<Mission> GetMissionsInConnection(LocationConnection connection)
@@ -101,16 +99,7 @@ namespace Barotrauma
public void ChangeType(LocationType newType)
{
if (newType == Type) { return; }
//clear missions from this and adjacent locations (they may be invalid now)
availableMissions.Clear();
foreach (LocationConnection connection in Connections)
{
connection.OtherLocation(this)?.availableMissions.Clear();
}
DebugConsole.Log("Location " + baseName + " changed it's type from " + Type + " to " + newType);
if (newType == Type) return;
Type = newType;
Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName);
@@ -122,7 +111,6 @@ namespace Barotrauma
{
if (mission.Completed)
{
DebugConsole.Log("Mission \"" + mission.Name + "\" completed in \"" + Name + "\".");
MissionsCompleted++;
}
}
@@ -130,10 +118,10 @@ namespace Barotrauma
availableMissions.RemoveAll(m => m.Completed);
}
private string RandomName(LocationType type, Random rand)
private string RandomName(LocationType type)
{
baseName = type.GetRandomName(rand);
nameFormatIndex = rand.Next() % type.NameFormats.Count;
baseName = type.GetRandomName();
nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server);
return type.NameFormats[nameFormatIndex].Replace("[name]", baseName);
}
@@ -155,15 +155,20 @@ namespace Barotrauma
return portraits[Math.Abs(portraitId) % portraits.Count];
}
public string GetRandomName()
{
return names[Rand.Int(names.Count, Rand.RandSync.Server)];
}
public static LocationType Random(string seed = "", int? zone = null)
{
Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
public string GetRandomName(Random rand)
{
return names[rand.Next() % names.Count];
}
public static LocationType Random(Random rand, int? zone = null)
{
Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
List<LocationType> allowedLocationTypes = zone.HasValue ? List.FindAll(lt => lt.CommonnessPerZone.ContainsKey(zone.Value)) : List;
if (allowedLocationTypes.Count == 0)
@@ -175,12 +180,12 @@ namespace Barotrauma
{
return ToolBox.SelectWeightedRandom(
allowedLocationTypes,
allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(),
rand);
allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(),
Rand.RandSync.Server);
}
else
{
return allowedLocationTypes[rand.Next() % allowedLocationTypes.Count];
return allowedLocationTypes[Rand.Int(allowedLocationTypes.Count, Rand.RandSync.Server)];
}
}
@@ -196,7 +196,7 @@ namespace Barotrauma
Vector2 position = points[positionIndex];
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) position = points[1 - positionIndex];
int zone = MathHelper.Clamp(generationParams.DifficultyZones - (int)Math.Floor(Vector2.Distance(position, mapCenter) / zoneRadius), 1, generationParams.DifficultyZones);
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.Server));
newLocations[i] = Location.CreateRandom(position, zone);
Locations.Add(newLocations[i]);
}
@@ -578,12 +578,10 @@ namespace Barotrauma
location.MissionsCompleted = missionsCompleted;
if (showNotifications && prevLocationType != location.Type)
{
var change = prevLocationType.CanChangeTo.Find(c =>
c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant());
if (change != null)
{
ChangeLocationType(location, prevLocationName, change);
}
ChangeLocationType(
location,
prevLocationName,
prevLocationType.CanChangeTo.Find(c => c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant()));
}
break;
case "connection":
@@ -297,13 +297,7 @@ namespace Barotrauma
CreateStairBodies();
}
}
// Only add ai targets automatically to walls
if (aiTarget == null && HasBody && Tags.Contains("wall"))
{
aiTarget = new AITarget(this);
}
InsertToList();
}
@@ -217,10 +217,6 @@ namespace Barotrauma
}
SerializableProperty.DeserializeProperties(sp, element);
if (sp.Body)
{
sp.Tags.Add("wall");
}
string translatedDescription = TextManager.Get("EntityDescription." + sp.identifier, true);
if (!string.IsNullOrEmpty(translatedDescription)) sp.Description = translatedDescription;
@@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Voronoi2;
@@ -494,7 +493,7 @@ namespace Barotrauma
}
}
public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null, float subDockingPortOffset = 0.0f)
public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null)
{
Rectangle dockedBorders = GetDockedBorders();
Vector2 diffFromDockedBorders =
@@ -542,17 +541,17 @@ namespace Barotrauma
else if (minX < 0)
{
//no wall found at the left side, spawn to the left from the right-side wall
spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset;
spawnPos.X = maxX - minWidth - 100.0f;
}
else if (maxX > Level.Loaded.Size.X)
{
//no wall found at right side, spawn to the right from the left-side wall
spawnPos.X = minX + minWidth + 100.0f + subDockingPortOffset;
spawnPos.X = minX + minWidth + 100.0f;
}
else
{
//walls found at both sides, use their midpoint
spawnPos.X = (minX + maxX) / 2 + subDockingPortOffset;
spawnPos.X = (minX + maxX) / 2;
}
spawnPos.Y = Math.Min(spawnPos.Y, Level.Loaded.Size.Y - dockedBorders.Height / 2 - 10);
@@ -666,7 +665,7 @@ namespace Barotrauma
}
}
public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null, bool allowInsideFixture = false)
public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
{
if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f)
{
@@ -678,20 +677,21 @@ namespace Barotrauma
Body closestBody = null;
if (allowInsideFixture)
{
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) =>
{
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem) return -1;
if (customPredicate != null && !customPredicate(fixture)) return -1;
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1;
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
closestFraction = 0.0f;
closestNormal = Vector2.Normalize(rayEnd - rayStart);
if (fixture.Body != null) closestBody = fixture.Body;
return false;
}, ref aabb);
if (closestFraction <= 0.0f)
if (fixture.Body.UserData is Structure structure)
{
lastPickedPosition = rayStart;
lastPickedFraction = closestFraction;
@@ -721,7 +721,7 @@ namespace Barotrauma
return closestBody;
}
public static List<Body> PickBodies(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null, bool allowInsideFixture = false)
public static List<Body> PickBodies(Vector2 rayStart, Vector2 rayEnd, List<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
{
if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f)
{
@@ -732,9 +732,25 @@ namespace Barotrauma
List<Body> bodies = new List<Body>();
GameMain.World.RayCast((fixture, point, normal, fraction) =>
{
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return -1; }
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem) return -1;
if (fixture.Body != null) { bodies.Add(fixture.Body); }
if (customPredicate != null && !customPredicate(fixture)) return -1;
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1;
if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1;
if (fixture.Body.UserData is Structure structure)
{
if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform)) return -1;
}
bodies.Add(fixture.Body);
if (fraction < closestFraction)
{
lastPickedPosition = rayStart + (rayEnd - rayStart) * fraction;
@@ -744,68 +760,10 @@ namespace Barotrauma
return fraction;
}, rayStart, rayEnd);
if (allowInsideFixture)
{
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) =>
{
if (bodies.Contains(fixture.Body) || fixture.Body == null) { return true; }
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
closestFraction = 0.0f;
lastPickedPosition = rayStart;
lastPickedFraction = 0.0f;
lastPickedNormal = Vector2.Normalize(rayEnd - rayStart);
bodies.Add(fixture.Body);
return false;
}, ref aabb);
}
return bodies;
}
private static bool CheckFixtureCollision(Fixture fixture, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
{
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem)
{
return false;
}
if (customPredicate != null && !customPredicate(fixture))
{
return false;
}
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories))
{
return false;
}
if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body))
{
return false;
}
if (fixture.Body.UserData is Structure structure)
{
if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform))
{
return false;
}
}
return true;
}
/// <summary>
/// check visibility between two points (in sim units)
/// </summary>
@@ -615,7 +615,7 @@ namespace Barotrauma
ID = (ushort)int.Parse(element.Attribute("ID").Value)
};
w.spawnType = spawnType;
Enum.TryParse(element.GetAttributeString("spawn", "Path"), out w.spawnType);
string idCardDescString = element.GetAttributeString("idcarddesc", "");
if (!string.IsNullOrWhiteSpace(idCardDescString))