v0.12.0.2

This commit is contained in:
Joonas Rikkonen
2021-02-10 17:08:21 +02:00
parent 5c80a59bdd
commit 694cdfee7b
353 changed files with 12897 additions and 5028 deletions
@@ -18,7 +18,7 @@ namespace Barotrauma
public static bool ShowWayPoints = true, ShowSpawnPoints = true;
public const float LadderWaypointInterval = 100.0f;
public const float LadderWaypointInterval = 70.0f;
protected SpawnType spawnType;
private string[] idCardTags;
@@ -99,6 +99,13 @@ namespace Barotrauma
{
SpawnType = SpawnType.Path;
}
#if CLIENT
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { this }, false));
}
#endif
}
@@ -179,42 +186,55 @@ namespace Barotrauma
}
}
float diffFromHullEdge = 50;
float minDist = 150.0f;
float minDist = 100.0f;
float heightFromFloor = 110.0f;
float hullMinHeight = 100;
foreach (Hull hull in Hull.hullList)
{
if (hull.Rect.Height < 150) { continue; }
WayPoint prevWaypoint = null;
// Ignore hulls that a human couldn't fit in.
// Doesn't take multi-hull rooms into account, but it's probably best to leave them to be setup manually.
if (hull.Rect.Height < hullMinHeight) { continue; }
// Don't create waypoints if there's no floor.
Vector2 floorPos = new Vector2(hull.SimPosition.X, ConvertUnits.ToSimUnits(hull.Rect.Y - hull.RectHeight - 50));
Body floor = Submarine.PickBody(hull.SimPosition, floorPos, collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform, customPredicate: f => !(f.Body.UserData is Submarine));
if (floor == null) { continue; }
// Make sure that the waypoints don't go higher than the halfway of the room.
float waypointHeight = hull.Rect.Height > heightFromFloor * 2 ? heightFromFloor : hull.Rect.Height / 2;
if (hull.Rect.Width < diffFromHullEdge * 3.0f)
{
new WayPoint(
new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);
continue;
new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
}
for (float x = hull.Rect.X + diffFromHullEdge; x <= hull.Rect.Right - diffFromHullEdge; x += minDist)
else
{
var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);
if (prevWaypoint != null) { wayPoint.ConnectTo(prevWaypoint); }
prevWaypoint = wayPoint;
WayPoint prevWaypoint = null;
for (float x = hull.Rect.X + diffFromHullEdge; x <= hull.Rect.Right - diffFromHullEdge; x += minDist)
{
var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
if (prevWaypoint != null) { wayPoint.ConnectTo(prevWaypoint); }
prevWaypoint = wayPoint;
}
if (prevWaypoint == null)
{
// Ensure that we always create at least one waypoint per hull.
new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
}
}
}
float outSideWaypointInterval = 200.0f;
float outSideWaypointInterval = 100.0f;
if (submarine.Info.Type != SubmarineType.OutpostModule)
{
int outsideWaypointDist = 100;
List<WayPoint> outsideWaypoints = new List<WayPoint>();
Rectangle borders = Hull.GetBorders();
borders.X -= outsideWaypointDist;
borders.Y += outsideWaypointDist;
borders.Width += outsideWaypointDist * 2;
borders.Height += outsideWaypointDist * 2;
int originalWidth = borders.Width;
int originalHeight = borders.Height;
borders.X -= Math.Min(500, originalWidth / 4);
borders.Y += Math.Min(500, originalHeight / 4);
borders.Width += Math.Min(1500, originalWidth / 2);
borders.Height += Math.Min(1000, originalHeight / 2);
borders.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition);
if (borders.Width <= outSideWaypointInterval * 2)
@@ -238,6 +258,8 @@ namespace Barotrauma
new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
SpawnType.Path, submarine);
outsideWaypoints.Add(wayPoint);
if (x == borders.X + outSideWaypointInterval)
{
cornerWaypoint[i, 0] = wayPoint;
@@ -260,18 +282,107 @@ namespace Barotrauma
new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
SpawnType.Path, submarine);
outsideWaypoints.Add(wayPoint);
if (y == borders.Y - borders.Height)
{
wayPoint.ConnectTo(cornerWaypoint[1, i]);
}
else
{
wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count - 2]);
wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]);
}
}
wayPoint.ConnectTo(cornerWaypoint[0, i]);
}
Vector2 center = ConvertUnits.ToSimUnits(submarine.HiddenSubPosition);
float halfHeight = ConvertUnits.ToSimUnits(borders.Height / 2);
// Try to move the waypoints so that they are near the walls, roughly following the shape of the sub.
foreach (WayPoint wp in outsideWaypoints)
{
float xDiff = center.X - wp.SimPosition.X;
Vector2 targetPos = new Vector2(center.X - xDiff * 0.5f, center.Y);
Body wall = Submarine.PickBody(wp.SimPosition, targetPos, collisionCategory: Physics.CollisionWall, customPredicate: f => !(f.Body.UserData is Submarine));
if (wall == null)
{
// Try again, and shoot to the center now. It happens with some subs that the first, offset raycast don't hit the walls.
targetPos = new Vector2(center.X - xDiff, center.Y);
wall = Submarine.PickBody(wp.SimPosition, targetPos, collisionCategory: Physics.CollisionWall, customPredicate: f => !(f.Body.UserData is Submarine));
}
if (wall != null)
{
float distanceFromWall = 1;
if (xDiff > 0 && !submarine.Info.HasTag(SubmarineTag.Shuttle))
{
// We don't want to move the waypoints near the tail too close to the engine.
float yDist = Math.Abs(center.Y - wp.SimPosition.Y);
distanceFromWall = MathHelper.Lerp(1, 3, MathUtils.InverseLerp(halfHeight, 0, yDist));
}
Vector2 newPos = Submarine.LastPickedPosition + Submarine.LastPickedNormal * distanceFromWall;
wp.rect = new Rectangle(ConvertUnits.ToDisplayUnits(newPos).ToPoint(), wp.rect.Size);
wp.FindHull();
}
}
// Remove unwanted points
var removals = new List<WayPoint>();
WayPoint previous = null;
float tooClose = outSideWaypointInterval / 2;
foreach (WayPoint wp in outsideWaypoints)
{
if (wp.CurrentHull != null ||
Submarine.PickBody(wp.SimPosition, wp.SimPosition + Vector2.Normalize(center - wp.SimPosition) * 0.1f, collisionCategory: Physics.CollisionWall | Physics.CollisionItem, customPredicate: f => !(f.Body.UserData is Submarine), allowInsideFixture: true) != null)
{
// Remove waypoints that got inside/too near the sub.
removals.Add(wp);
previous = wp;
continue;
}
foreach (WayPoint otherWp in outsideWaypoints)
{
if (otherWp == wp) { continue; }
if (removals.Contains(otherWp)) { continue; }
float sqrDist = Vector2.DistanceSquared(wp.Position, otherWp.Position);
// Remove waypoints that are too close to each other.
if (!removals.Contains(previous) && sqrDist < tooClose * tooClose)
{
removals.Add(wp);
}
}
previous = wp;
}
foreach (WayPoint wp in removals)
{
outsideWaypoints.Remove(wp);
wp.Remove();
}
// Connect loose ends (TODO: this sometimes fails, creating the connection to a wrong node)
for (int i = 0; i < outsideWaypoints.Count; i++)
{
WayPoint current = outsideWaypoints[i];
if (current.linkedTo.Count > 1) { continue; }
WayPoint next = null;
int maxConnections = 2;
float tooFar = outSideWaypointInterval * 5;
for (int j = 0; j < maxConnections; j++)
{
if (current.linkedTo.Count >= maxConnections) { break; }
tooFar /= current.linkedTo.Count;
// First try to find a loose end
next = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp != next && wp.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.linkedTo.Count < 2);
// Then accept any connection that not connected to the existing connection
next ??= current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp != next && wp.linkedTo.None(e => current.linkedTo.Contains(e)));
if (next != null)
{
current.ConnectTo(next);
}
}
if (current.linkedTo.Count == 1)
{
DebugConsole.ThrowError($"Couldn't automatically link waypoint {current.ID}. You should do it manually.");
}
}
}
List<Structure> stairList = new List<Structure>();
@@ -298,13 +409,13 @@ namespace Barotrauma
{
for (int dir = -1; dir <= 1; dir += 2)
{
WayPoint closest = stairPoints[i].FindClosest(dir, true, new Vector2(-30.0f, 30f));
if (closest == null) continue;
WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(100, 70));
if (closest == null) { continue; }
stairPoints[i].ConnectTo(closest);
}
}
stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position)/2, SpawnType.Path, submarine);
stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine);
stairPoints[0].ConnectTo(stairPoints[2]);
stairPoints[2].ConnectTo(stairPoints[1]);
}
@@ -314,21 +425,44 @@ namespace Barotrauma
var ladders = item.GetComponent<Ladder>();
if (ladders == null) { continue; }
Vector2 bottomPoint = new Vector2(item.Rect.Center.X, item.Rect.Top - item.Rect.Height + 10);
List<WayPoint> ladderPoints = new List<WayPoint>
{
new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height + heightFromFloor), SpawnType.Path, submarine)
new WayPoint(bottomPoint, SpawnType.Path, submarine),
};
WayPoint prevPoint = ladderPoints[0];
Vector2 prevPos = prevPoint.SimPosition;
List<Body> ignoredBodies = new List<Body>();
for (float y = ladderPoints[0].Position.Y + LadderWaypointInterval; y < item.Rect.Y - 1.0f; y += LadderWaypointInterval)
// Lowest point is only meaningful for hanging ladders inside the sub, but it shouldn't matter in other cases either.
// Start point is where the bots normally grasp the ladder when they stand on ground.
WayPoint lowestPoint = ladderPoints[0];
WayPoint prevPoint = lowestPoint;
Vector2 prevPos = prevPoint.SimPosition;
Body ground = Submarine.PickBody(lowestPoint.SimPosition, lowestPoint.SimPosition - Vector2.UnitY, ignoredBodies,
collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform | Physics.CollisionStairs,
customPredicate: f => !(f.Body.UserData is Submarine));
float startHeight = ground != null ? ConvertUnits.ToDisplayUnits(ground.Position.Y) : bottomPoint.Y;
startHeight += heightFromFloor;
WayPoint startPoint = lowestPoint;
Vector2 nextPos = new Vector2(item.Rect.Center.X, startHeight);
// Don't create the start point if it's too close to the lowest point or if it's outside of the sub.
// If we skip creating the start point, the lowest point is used instead.
if (lowestPoint == null || Math.Abs(startPoint.Position.Y - startHeight) > 40 && Hull.FindHull(nextPos) != null)
{
startPoint = new WayPoint(nextPos, SpawnType.Path, submarine);
ladderPoints.Add(startPoint);
if (lowestPoint != null)
{
startPoint.ConnectTo(lowestPoint);
}
prevPoint = startPoint;
prevPos = prevPoint.SimPosition;
}
for (float y = startPoint.Position.Y + LadderWaypointInterval; y < item.Rect.Y - 1.0f; y += LadderWaypointInterval)
{
//first check if there's a door in the way
//(we need to create a waypoint linked to the door for NPCs to open it)
Body pickedBody = Submarine.PickBody(
ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)),
ConvertUnits.ToSimUnits(new Vector2(startPoint.Position.X, y)),
prevPos, ignoredBodies, Physics.CollisionWall, false,
(Fixture f) => f.Body.UserData is Item && ((Item)f.Body.UserData).GetComponent<Door>() != null);
@@ -341,7 +475,7 @@ namespace Barotrauma
{
//no door, check for walls
pickedBody = Submarine.PickBody(
ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)), prevPos, ignoredBodies, null, false,
ConvertUnits.ToSimUnits(new Vector2(startPoint.Position.X, y)), prevPos, ignoredBodies, null, false,
(Fixture f) => f.Body.UserData is Structure);
}
@@ -374,75 +508,94 @@ namespace Barotrauma
}
}
if (prevPoint.rect.Y < item.Rect.Y - 10.0f)
// Cap
if (prevPoint.rect.Y < item.Rect.Y - 40)
{
WayPoint newPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);
ladderPoints.Add(newPoint);
newPoint.ConnectTo(prevPoint);
WayPoint wayPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);
ladderPoints.Add(wayPoint);
wayPoint.ConnectTo(prevPoint);
}
//connect ladder waypoints to hull points at the right and left side
// Connect ladder waypoints to hull points at the right and left side
foreach (WayPoint ladderPoint in ladderPoints)
{
ladderPoint.Ladders = ladders;
//don't connect if the waypoint is at a gap (= at the boundary of hulls and/or at a hatch)
if (ladderPoint.ConnectedGap != null) continue;
bool isHatch = ladderPoint.ConnectedGap != null && !ladderPoint.ConnectedGap.IsRoomToRoom;
for (int dir = -1; dir <= 1; dir += 2)
{
WayPoint closest = ladderPoint.FindClosest(dir, true, new Vector2(-150.0f, 50f));
if (closest == null) continue;
WayPoint closest = null;
if (isHatch)
{
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null, ignored: ladderPoints);
}
else
{
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 70), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
}
if (closest == null) { continue; }
ladderPoint.ConnectTo(closest);
}
}
}
foreach (Gap gap in Gap.GapList)
// Another pass: connect cap and bottom points with other ladders when they are vertically adjacent to another (double ladders)
foreach (Item item in Item.ItemList)
{
if (!gap.IsHorizontal) continue;
//too small to walk through
if (gap.Rect.Height < 150.0f) continue;
var wayPoint = new WayPoint(
new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor), SpawnType.Path, submarine, gap);
for (int dir = -1; dir <= 1; dir += 2)
{
float tolerance = gap.IsRoomToRoom ? 50.0f : outSideWaypointInterval / 2.0f;
WayPoint closest = wayPoint.FindClosest(
dir, true, new Vector2(-tolerance, tolerance),
gap.ConnectedDoor?.Body.FarseerBody);
if (closest != null)
{
wayPoint.ConnectTo(closest);
}
}
var ladders = item.GetComponent<Ladder>();
if (ladders == null) { continue; }
var wps = WayPointList.Where(wp => wp.Ladders == ladders).OrderByDescending(wp => wp.Rect.Y);
WayPoint cap = wps.First();
WayPoint above = cap.FindClosest(1, horizontalSearch: false, tolerance: new Vector2(25, 50), filter: wp => wp.Ladders != null && wp.Ladders != ladders);
above?.ConnectTo(cap);
WayPoint bottom = wps.Last();
WayPoint below = bottom.FindClosest(-1, horizontalSearch: false, tolerance: new Vector2(25, 50), filter: wp => wp.Ladders != null && wp.Ladders != ladders);
below?.ConnectTo(bottom);
}
foreach (Gap gap in Gap.GapList)
{
if (gap.IsHorizontal || gap.IsRoomToRoom || !gap.linkedTo.Any(l => l is Hull)) { continue; }
//too small to walk through
if (gap.Rect.Width < 100.0f) { continue; }
var wayPoint = new WayPoint(
new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2), SpawnType.Path, submarine, gap);
float tolerance = outSideWaypointInterval / 2.0f;
Hull connectedHull = (Hull)gap.linkedTo.First(l => l is Hull);
int dir = Math.Sign(connectedHull.Position.Y - gap.Position.Y);
WayPoint closest = wayPoint.FindClosest(
dir, false, new Vector2(-tolerance, tolerance),
gap.ConnectedDoor?.Body.FarseerBody);
if (closest != null)
if (gap.IsHorizontal)
{
wayPoint.ConnectTo(closest);
// Too small to walk through
if (gap.Rect.Height < hullMinHeight) { continue; }
Vector2 pos = new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor);
var wayPoint = new WayPoint(pos, SpawnType.Path, submarine, gap);
// The closest waypoint can be quite far if the gap is at an exterior door.
Vector2 tolerance = gap.IsRoomToRoom ? new Vector2(150, 70) : new Vector2(1000, 1000);
for (int dir = -1; dir <= 1; dir += 2)
{
WayPoint closest = wayPoint.FindClosest(dir, horizontalSearch: true, tolerance, gap.ConnectedDoor?.Body.FarseerBody);
if (closest != null)
{
wayPoint.ConnectTo(closest);
}
}
}
else
{
// Create waypoints on vertical gaps on the outer walls, also hatches.
if (gap.IsRoomToRoom || gap.linkedTo.None(l => l is Hull)) { continue; }
// Too small to swim through
if (gap.Rect.Width < 50.0f) { continue; }
Vector2 pos = new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2);
// Some hatches are created in the block above where we handle the ladder waypoints. So we need to check for duplicates.
if (WayPointList.Any(wp => wp.ConnectedGap == gap)) { continue; }
var wayPoint = new WayPoint(pos, SpawnType.Path, submarine, gap);
Hull connectedHull = (Hull)gap.linkedTo.First(l => l is Hull);
int dir = Math.Sign(connectedHull.Position.Y - gap.Position.Y);
WayPoint closest = wayPoint.FindClosest(dir, horizontalSearch: false, new Vector2(50, 100));
if (closest != null)
{
wayPoint.ConnectTo(closest);
}
for (dir = -1; dir <= 1; dir += 2)
{
closest = wayPoint.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), gap.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null);
if (closest != null)
{
wayPoint.ConnectTo(closest);
}
}
}
}
@@ -462,7 +615,36 @@ namespace Barotrauma
return true;
}
private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null)
private WayPoint FindClosestOutside(IEnumerable<WayPoint> waypointList, float tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<WayPoint, bool> filter = null)
{
float closestDist = 0;
WayPoint closest = null;
foreach (WayPoint wp in waypointList)
{
if (wp.SpawnType != SpawnType.Path || wp == this) { continue; }
// Ignore if already linked
if (linkedTo.Contains(wp)) { continue; }
if (ignored != null && ignored.Contains(wp)) { continue; }
if (filter != null && !filter(wp)) { continue; }
float sqrDist = Vector2.DistanceSquared(Position, wp.Position);
if (closest == null || sqrDist < closestDist)
{
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
if (body != null && body != ignoredBody && !(body.UserData is Submarine))
{
if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
{
continue;
}
}
closestDist = sqrDist;
closest = wp;
}
}
return closest;
}
private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<WayPoint, bool> filter = null)
{
if (dir != -1 && dir != 1) { return null; }
@@ -473,33 +655,45 @@ namespace Barotrauma
{
if (wp.SpawnType != SpawnType.Path || wp == this) { continue; }
float xDiff = wp.Position.X - Position.X;
float yDiff = wp.Position.Y - Position.Y;
float xDist = Math.Abs(xDiff);
float yDist = Math.Abs(yDiff);
if (tolerance.X < xDist) { continue; }
if (tolerance.Y < yDist) { continue; }
float dist = 0.0f;
float diff = 0.0f;
if (horizontalSearch)
{
if ((wp.Position.Y - Position.Y) < tolerance.X || (wp.Position.Y - Position.Y) > tolerance.Y) { continue; }
diff = wp.Position.X - Position.X;
dist = Math.Abs(diff) + Math.Abs(wp.Position.Y - Position.Y) / 5.0f;
diff = xDiff;
dist = xDist + yDist / 5.0f;
}
else
{
if ((wp.Position.X - Position.X) < tolerance.X || (wp.Position.X - Position.X) > tolerance.Y) { continue; }
diff = wp.Position.Y - Position.Y;
dist = Math.Abs(diff) + Math.Abs(wp.Position.X - Position.X) / 5.0f;
diff = yDiff;
dist = yDist + xDist / 5.0f;
//prefer ladder waypoints when moving vertically
if (wp.Ladders != null) { dist *= 0.5f; }
}
if (Math.Sign(diff) != dir) { continue; }
// Ignore if already linked
if (linkedTo.Contains(wp)) { continue; }
if (ignored != null && ignored.Contains(wp)) { continue; }
if (filter != null && !filter(wp)) { continue; }
if (closest == null || dist < closestDist)
{
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, true, true, false);
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
if (body != null && body != ignoredBody && !(body.UserData is Submarine))
{
if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall)) { continue; }
if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
{
continue;
}
}
closestDist = dist;
closest = wp;
}