Unstable 0.1400.0.0

This commit is contained in:
Markus Isberg
2021-05-11 15:47:47 +03:00
parent 3f324b14e8
commit 92f0264af2
247 changed files with 8238 additions and 1911 deletions
@@ -189,6 +189,10 @@ namespace Barotrauma
if (roomName == value) { return; }
roomName = value;
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
if (!IsWetRoom && ForceAsWetRoom)
{
IsWetRoom = true;
}
}
}
@@ -329,6 +333,42 @@ namespace Barotrauma
}
}
private bool ForceAsWetRoom =>
roomName != null && (
roomName.Contains("ballast", StringComparison.OrdinalIgnoreCase) ||
roomName.Contains("bilge", StringComparison.OrdinalIgnoreCase) ||
roomName.Contains("airlock", StringComparison.OrdinalIgnoreCase));
private bool isWetRoom;
[Editable, Serialize(false, true, description: "It's normal for this hull to be filled with water. If the room name contains 'ballast', 'bilge', or 'airlock', you can't disable this setting.")]
public bool IsWetRoom
{
get { return isWetRoom; }
set
{
isWetRoom = value;
if (ForceAsWetRoom)
{
isWetRoom = true;
}
}
}
private bool avoidStaying;
[Editable, Serialize(false, true, description: "Bots avoid staying here, but they are still allowed to access the room when needed and go through it. Forced true for wet rooms.")]
public bool AvoidStaying
{
get { return avoidStaying || IsWetRoom; }
set
{
avoidStaying = value;
if (IsWetRoom)
{
avoidStaying = true;
}
}
}
public float WaterPercentage => MathUtils.Percentage(WaterVolume, Volume);
public float OxygenPercentage
@@ -325,9 +325,11 @@ namespace Barotrauma
get { return LevelData.Seed; }
}
public static float? ForcedDifficulty;
public float Difficulty
{
get { return LevelData.Difficulty; }
get { return ForcedDifficulty ?? LevelData.Difficulty; }
}
public LevelData.LevelType Type
@@ -2710,14 +2712,21 @@ namespace Barotrauma
return position;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
public bool TryGetInterestingPositionAwayFromPoint(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Vector2 awayPoint, float minDistFromPoint, Func<InterestingPosition, bool> filter = null)
{
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, filter);
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, awayPoint, minDistFromPoint, filter);
position = pos.ToVector2();
return success;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Func<InterestingPosition, bool> filter = null)
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
{
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, Vector2.Zero, minDistFromPoint: 0, filter);
position = pos.ToVector2();
return success;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Vector2 awayPoint, float minDistFromPoint = 0f, Func<InterestingPosition, bool> filter = null)
{
if (!PositionsOfInterest.Any())
{
@@ -2755,6 +2764,11 @@ namespace Barotrauma
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), sub.WorldPosition) < minDistFromSubs * minDistFromSubs);
}
}
if (minDistFromPoint > 0.0f)
{
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), awayPoint) < minDistFromPoint * minDistFromPoint);
}
if (!farEnoughPositions.Any())
{
string errorMsg = "Could not find a position of interest far enough from the submarines. (PositionType: " + positionType + ", minDistFromSubs: " + minDistFromSubs + ")\n" + Environment.StackTrace.CleanupStackTrace();
@@ -2826,7 +2840,7 @@ namespace Barotrauma
if (index < 0 || index >= bottomPositions.Count - 1) { return new Vector2(xPosition, BottomPos); }
float t = (xPosition - bottomPositions[index].X) / (bottomPositions[index + 1].X - bottomPositions[index].X);
Debug.Assert(t < 1.0f);
Debug.Assert(t <= 1.0f);
t = MathHelper.Clamp(t, 0.0f, 1.0f);
float yPos = MathHelper.Lerp(bottomPositions[index].Y, bottomPositions[index + 1].Y, t);
@@ -3094,12 +3108,11 @@ namespace Barotrauma
sub.SetPosition(spawnPoint);
wreckPositions.Add(sub, positions);
blockedRects.Add(sub, rects);
return sub;
}
else
{
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds.ToString()} (ms).", Color.Red);
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds} (ms).", Color.Red);
return null;
}
@@ -3306,18 +3319,19 @@ namespace Barotrauma
}
wreckFiles.Shuffle(Rand.RandSync.Server);
int wreckCount = Math.Min(Loaded.GenerationParams.WreckCount, wreckFiles.Count);
int minWreckCount = Math.Min(Loaded.GenerationParams.MinWreckCount, wreckFiles.Count);
int maxWreckCount = Math.Min(Loaded.GenerationParams.MaxWreckCount, wreckFiles.Count);
int wreckCount = Rand.Range(minWreckCount, maxWreckCount, Rand.RandSync.Server);
Wrecks = new List<Submarine>(wreckCount);
for (int i = 0; i < wreckCount; i++)
{
ContentFile contentFile = wreckFiles[i];
if (contentFile == null) { continue; }
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
// For storing the translations. Used only for debugging.
SpawnSubOnPath(wreckName, contentFile, SubmarineType.Wreck);
}
totalSW.Stop();
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds.ToString()} (ms)");
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds} (ms)");
}
private bool HasStartOutpost()
@@ -3365,11 +3379,8 @@ namespace Barotrauma
for (int i = 0; i < 2; i++)
{
if (Submarine.MainSubs.Length > 1 && Submarine.MainSubs[0] != null && Submarine.MainSubs[1] != null)
{
continue;
}
if (GameMain.GameSession.GameMode is PvPMode) { continue; }
bool isStart = (i == 0) == !Mirrored;
if (isStart)
{
@@ -496,8 +496,11 @@ namespace Barotrauma
[Serialize(1, true, description: "The number of alien ruins in the level."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int RuinCount { get; set; }
[Serialize(1, true, description: "The minimum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int MinWreckCount { get; set; }
[Serialize(1, true, description: "The maximum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int WreckCount { get; set; }
public int MaxWreckCount { get; set; }
// TODO: Move the wreck parameters under a separate class?
#region Wreck parameters
@@ -21,6 +21,12 @@ namespace Barotrauma
private List<LevelObject> updateableObjects;
private List<LevelObject>[,] objectGrid;
public float GlobalForceDecreaseTimer
{
get;
private set;
}
public LevelObjectManager() : base(null, Entity.NullEntityID)
{
}
@@ -133,10 +139,24 @@ namespace Barotrauma
suitableSpawnPositions.Add(prefab,
availableSpawnPositions.Where(sp =>
sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
sp.Length >= prefab.MinSurfaceWidth &&
sp.Length >= prefab.MinSurfaceWidth &&
(prefab.AllowAtStart || !closeToStart(sp.GraphEdge.Center)) &&
(prefab.AllowAtEnd || !closeToEnd(sp.GraphEdge.Center)) &&
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
spawnPositionWeights.Add(prefab,
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
bool closeToStart(Vector2 position)
{
float minDist = level.Size.X * 0.2f;
return MathUtils.LineSegmentToPointDistanceSquared(level.StartPosition.ToPoint(), level.StartExitPosition.ToPoint(), position.ToPoint()) < minDist * minDist;
}
bool closeToEnd(Vector2 position)
{
float minDist = level.Size.X * 0.2f;
return MathUtils.LineSegmentToPointDistanceSquared(level.EndPosition.ToPoint(), level.EndExitPosition.ToPoint(), position.ToPoint()) < minDist * minDist;
}
}
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
@@ -484,6 +504,12 @@ namespace Barotrauma
public void Update(float deltaTime)
{
GlobalForceDecreaseTimer += deltaTime;
if (GlobalForceDecreaseTimer > 1000000.0f)
{
GlobalForceDecreaseTimer = 0.0f;
}
foreach (LevelObject obj in updateableObjects)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
@@ -176,6 +176,20 @@ namespace Barotrauma
private set;
}
[Editable, Serialize(true, true, description: "Can the object be placed near the start of the level.")]
public bool AllowAtStart
{
get;
private set;
}
[Editable, Serialize(true, true, description: "Can the object be placed near the end of the level.")]
public bool AllowAtEnd
{
get;
private set;
}
[Serialize(0.0f, true, description: "Minimum length of a graph edge the object can spawn on."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
/// <summary>
/// Minimum length of a graph edge the object can spawn on.
@@ -140,6 +140,11 @@ namespace Barotrauma
get;
private set;
}
public float GlobalForceDecreaseInterval
{
get;
private set;
}
private readonly TriggerForceMode forceMode;
public TriggerForceMode ForceMode
@@ -234,6 +239,7 @@ namespace Barotrauma
ForceFluctuationInterval = element.GetAttributeFloat("forcefluctuationinterval", 0.01f);
ForceFluctuationStrength = Math.Max(element.GetAttributeFloat("forcefluctuationstrength", 0.0f), 0.0f);
ForceFalloff = element.GetAttributeBool("forcefalloff", true);
GlobalForceDecreaseInterval = element.GetAttributeFloat("globalforcedecreaseinterval", 0.0f);
ForceVelocityLimit = ConvertUnits.ToSimUnits(element.GetAttributeFloat("forcevelocitylimit", float.MaxValue));
string forceModeStr = element.GetAttributeString("forcemode", "Force");
@@ -434,6 +440,8 @@ namespace Barotrauma
}
}
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public void Update(float deltaTime)
{
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
@@ -457,7 +465,13 @@ namespace Barotrauma
if (!UseNetworkSyncing || isNotClient)
{
if (ForceFluctuationStrength > 0.0f)
if (GlobalForceDecreaseInterval > 0.0f && Level.Loaded?.LevelObjectManager != null &&
Level.Loaded.LevelObjectManager.GlobalForceDecreaseTimer % (GlobalForceDecreaseInterval * 2) < GlobalForceDecreaseInterval)
{
NeedsNetworkSyncing |= currentForceFluctuation > 0.0f;
currentForceFluctuation = 0.0f;
}
else 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())
@@ -533,8 +547,8 @@ namespace Barotrauma
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
{
var targets = new List<ISerializableEntity>();
effect.GetNearbyTargets(worldPosition, targets);
targets.Clear();
targets.AddRange(effect.GetNearbyTargets(worldPosition, targets));
effect.Apply(effect.type, deltaTime, triggerer, targets);
}
}
@@ -533,8 +533,24 @@ namespace Barotrauma
//prefer connections that haven't been passed through, and connections with fewer available missions
connection = ToolBox.SelectWeightedRandom(
suitableConnections.ToList(),
suitableConnections.Select(c => (c.Passed ? 1.0f : 5.0f) / Math.Max(availableMissions.Count(m => m.Locations.Contains(c.OtherLocation(this))), 1.0f)).ToList(),
Rand.RandSync.Unsynced);
suitableConnections.Select(c => GetConnectionWeight(this, c)).ToList(),
Rand.RandSync.Unsynced);
static float GetConnectionWeight(Location location, LocationConnection c)
{
float weight = c.Passed ? 1.0f : 5.0f;
Location destination = c.OtherLocation(location);
if (destination != null)
{
if (destination.MapPosition.X > location.MapPosition.X) { weight *= 2.0f; }
int missionCount = location.availableMissions.Count(m => m.Locations.Contains(destination));
if (missionCount > 0)
{
weight /= missionCount * 2;
}
}
return weight;
}
return InstantiateMission(prefab, connection);
}
@@ -542,14 +558,14 @@ namespace Barotrauma
private Mission InstantiateMission(MissionPrefab prefab, LocationConnection connection)
{
Location destination = connection.OtherLocation(this);
var mission = prefab.Instantiate(new Location[] { this, destination });
var mission = prefab.Instantiate(new Location[] { this, destination }, Submarine.MainSub);
mission.AdjustLevelData(connection.LevelData);
return mission;
}
private Mission InstantiateMission(MissionPrefab prefab)
{
var mission = prefab.Instantiate(new Location[] { this, this });
var mission = prefab.Instantiate(new Location[] { this, this }, Submarine.MainSub);
mission.AdjustLevelData(LevelData);
return mission;
}
@@ -570,7 +586,7 @@ namespace Barotrauma
{
destination = Connections.First().OtherLocation(this);
}
var mission = loadedMission.MissionPrefab.Instantiate(new Location[] { this, destination });
var mission = loadedMission.MissionPrefab.Instantiate(new Location[] { this, destination }, Submarine.MainSub);
availableMissions.Add(mission);
if (loadedMission.SelectedMission) { SelectedMission = mission; }
}
@@ -81,6 +81,7 @@ namespace Barotrauma
if (location.Type.HasOutpost && !wasCritical && location.IsCriticallyRadiated())
{
location.ClearMissions();
amountOfOutposts--;
}
}
@@ -280,7 +280,7 @@ namespace Barotrauma
public void ResolveLinks(IdRemap childRemap)
{
if (unresolvedLinkedToID == null) { return; }
for (int i=0;i<unresolvedLinkedToID.Count;i++)
for (int i = 0; i < unresolvedLinkedToID.Count; i++)
{
int srcId = unresolvedLinkedToID[i];
int targetId = childRemap.GetOffsetId(srcId);
@@ -649,7 +649,10 @@ namespace Barotrauma
else
{
object newEntity = loadMethod.Invoke(t, new object[] { element, submarine, idRemap });
if (newEntity != null) entities.Add((MapEntity)newEntity);
if (newEntity != null)
{
entities.Add((MapEntity)newEntity);
}
}
}
catch (TargetInvocationException e)
@@ -1238,6 +1238,26 @@ namespace Barotrauma
return list.FindAll(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
}
public List<(ItemContainer container, int freeSlots)> GetCargoContainers()
{
List<(ItemContainer container, int freeSlots)> containers = new List<(ItemContainer container, int freeSlots)>();
var connectedSubs = GetConnectedSubs();
foreach (Item item in Item.ItemList)
{
if (!connectedSubs.Contains(item.Submarine)) { continue; }
if (!item.HasTag("cargocontainer")) { continue; }
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer == null) { continue; }
int emptySlots = 0;
for (int i = 0; i < itemContainer.Inventory.Capacity; i++)
{
if (itemContainer.Inventory.GetItemAt(i) == null) { emptySlots++; }
}
containers.Add((itemContainer, emptySlots));
}
return containers;
}
public IEnumerable<T> GetEntities<T>(bool includingConnectedSubs, IEnumerable<T> list) where T : MapEntity
{
return list.Where(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
@@ -1527,6 +1547,8 @@ namespace Barotrauma
Rectangle dimensions = CalculateDimensions();
element.Add(new XAttribute("dimensions", XMLExtensions.Vector2ToString(dimensions.Size.ToVector2())));
var cargoContainers = GetCargoContainers();
element.Add(new XAttribute("cargocapacity", cargoContainers.Sum(c => c.freeSlots)));
element.Add(new XAttribute("recommendedcrewsizemin", Info.RecommendedCrewSizeMin));
element.Add(new XAttribute("recommendedcrewsizemax", Info.RecommendedCrewSizeMax));
element.Add(new XAttribute("recommendedcrewexperience", Info.RecommendedCrewExperience ?? ""));
@@ -1537,6 +1559,40 @@ namespace Barotrauma
Info.OutpostModuleInfo?.Save(element);
}
foreach (Item item in Item.ItemList)
{
if (item.PendingItemSwap?.SwappableItem?.ConnectedItemsToSwap == null) { continue; }
foreach (var (requiredTag, swapTo) in item.PendingItemSwap.SwappableItem.ConnectedItemsToSwap)
{
List<Item> itemsToSwap = new List<Item>();
itemsToSwap.AddRange(item.linkedTo.Where(lt => (lt as Item)?.HasTag(requiredTag) ?? false).Cast<Item>());
var connectionPanel = item.GetComponent<ConnectionPanel>();
if (connectionPanel != null)
{
foreach (Connection c in connectionPanel.Connections)
{
foreach (var connectedComponent in item.GetConnectedComponentsRecursive<ItemComponent>(c))
{
if (!itemsToSwap.Contains(connectedComponent.Item) && connectedComponent.Item.HasTag(requiredTag))
{
itemsToSwap.Add(connectedComponent.Item);
}
}
}
}
ItemPrefab itemPrefab = ItemPrefab.Find("", swapTo);
if (itemPrefab == null)
{
DebugConsole.ThrowError($"Failed to swap an item connected to \"{item.Name}\" into \"{swapTo}\".");
continue;
}
foreach (Item itemToSwap in itemsToSwap)
{
if (itemPrefab != itemToSwap.Prefab) { itemToSwap.PendingItemSwap = itemPrefab; }
}
}
}
foreach (MapEntity e in MapEntity.mapEntityList.OrderBy(e => e.ID))
{
if (!e.ShouldBeSaved) { continue; }
@@ -174,6 +174,11 @@ namespace Barotrauma
float simWidth = ConvertUnits.ToSimUnits(width);
float simHeight = ConvertUnits.ToSimUnits(height);
if (sub.FlippedX)
{
simPos.X = -simPos.X;
}
if (width > 0.0f && height > 0.0f)
{
item.StaticFixtures.Add(farseerBody.CreateRectangle(simWidth, simHeight, 5.0f, simPos));
@@ -513,7 +518,7 @@ namespace Barotrauma
{
return CheckCharacterCollision(contact, character);
}
else if (f2.UserData is Items.Components.DockingPort)
else if (f1.UserData is Items.Components.DockingPort || f2.UserData is Items.Components.DockingPort)
{
return false;
}
@@ -133,6 +133,12 @@ namespace Barotrauma
private set;
}
public int CargoCapacity
{
get;
private set;
}
public string FilePath
{
get;
@@ -261,6 +267,7 @@ namespace Barotrauma
SubmarineClass = original.SubmarineClass;
hash = !string.IsNullOrEmpty(original.FilePath) ? original.MD5Hash : null;
Dimensions = original.Dimensions;
CargoCapacity = original.CargoCapacity;
FilePath = original.FilePath;
RequiredContentPackages = new HashSet<string>(original.RequiredContentPackages);
IsFileCorrupted = original.IsFileCorrupted;
@@ -323,6 +330,7 @@ namespace Barotrauma
Tags = tags;
}
Dimensions = SubmarineElement.GetAttributeVector2("dimensions", Vector2.Zero);
CargoCapacity = SubmarineElement.GetAttributeInt("cargocapacity", -1);
RecommendedCrewSizeMin = SubmarineElement.GetAttributeInt("recommendedcrewsizemin", 0);
RecommendedCrewSizeMax = SubmarineElement.GetAttributeInt("recommendedcrewsizemax", 0);
RecommendedCrewExperience = SubmarineElement.GetAttributeString("recommendedcrewexperience", "Unknown");
@@ -511,10 +519,14 @@ namespace Barotrauma
//saving/loading ----------------------------------------------------
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
{
var newElement = new XElement(SubmarineElement.Name,
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
var newElement = new XElement(
SubmarineElement.Name,
SubmarineElement.Attributes()
.Where(a =>
!string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
SubmarineElement.Elements());
if (Type == SubmarineType.OutpostModule)
{
OutpostModuleInfo.Save(newElement);
@@ -574,7 +586,7 @@ namespace Barotrauma
var contentPackageSubs = ContentPackage.GetFilesOfType(
GameMain.Config.AllEnabledPackages,
ContentType.Submarine, ContentType.Outpost, ContentType.OutpostModule,
ContentType.Wreck, ContentType.BeaconStation);
ContentType.Wreck, ContentType.BeaconStation, ContentType.EnemySubmarine);
for (int i = savedSubmarines.Count - 1; i >= 0; i--)
{
@@ -192,17 +192,37 @@ namespace Barotrauma
float minDist = 100.0f;
float heightFromFloor = 110.0f;
float hullMinHeight = 100;
foreach (Hull hull in Hull.hullList)
{
// 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));
// Do five raycasts to check if there's a floor. Don't create waypoints unless we can find a floor.
Body floor = null;
for (int i = 0; i < 5; i++)
{
float horizontalOffset = 0;
switch (i)
{
case 1:
horizontalOffset = hull.RectWidth * 0.2f;
break;
case 2:
horizontalOffset = hull.RectWidth * 0.4f;
break;
case 3:
horizontalOffset = -hull.RectWidth * 0.2f;
break;
case 4:
horizontalOffset = -hull.RectWidth * 0.4f;
break;
}
horizontalOffset = ConvertUnits.ToSimUnits(horizontalOffset);
Vector2 floorPos = new Vector2(hull.SimPosition.X + horizontalOffset, ConvertUnits.ToSimUnits(hull.Rect.Y - hull.RectHeight - 50));
floor = Submarine.PickBody(new Vector2(hull.SimPosition.X + horizontalOffset, hull.SimPosition.Y), floorPos, collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform, customPredicate: f => !(f.Body.UserData is Submarine));
if (floor != null) { break; }
}
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)
{
@@ -223,12 +243,42 @@ namespace Barotrauma
new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
}
}
}
}
// Platforms
foreach (Structure platform in Structure.WallList)
{
if (!platform.IsPlatform) { continue; }
float waypointHeight = heightFromFloor;
WayPoint prevWaypoint = null;
for (float x = platform.Rect.X + diffFromHullEdge; x <= platform.Rect.Right - diffFromHullEdge; x += minDist)
{
WayPoint wayPoint = new WayPoint(new Vector2(x, platform.Rect.Y + waypointHeight), SpawnType.Path, submarine);
if (prevWaypoint != null)
{
wayPoint.ConnectTo(prevWaypoint);
}
// If the waypoint is close to hull waypoints, remove it.
if (wayPoint != null)
{
for (int dir = -1; dir <= 1; dir += 2)
{
if (wayPoint.FindClosest(dir, horizontalSearch: true, tolerance: new Vector2(minDist, heightFromFloor), ignored: prevWaypoint.ToEnumerable()) != null)
{
wayPoint.Remove();
wayPoint = null;
break;
}
}
}
prevWaypoint = wayPoint;
}
}
float outSideWaypointInterval = 100.0f;
if (submarine.Info.Type != SubmarineType.OutpostModule)
{
List<WayPoint> outsideWaypoints = new List<WayPoint>();
List<(WayPoint, int)> outsideWaypoints = new List<(WayPoint, int)>();
Rectangle borders = Hull.GetBorders();
int originalWidth = borders.Width;
@@ -260,7 +310,7 @@ namespace Barotrauma
new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
SpawnType.Path, submarine);
outsideWaypoints.Add(wayPoint);
outsideWaypoints.Add((wayPoint, i));
if (x == borders.X + outSideWaypointInterval)
{
@@ -284,7 +334,7 @@ namespace Barotrauma
new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
SpawnType.Path, submarine);
outsideWaypoints.Add(wayPoint);
outsideWaypoints.Add((wayPoint, i));
if (y == borders.Y - borders.Height)
{
@@ -302,8 +352,9 @@ namespace Barotrauma
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)
foreach (var wayPoint in outsideWaypoints)
{
WayPoint wp = wayPoint.Item1;
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));
@@ -331,8 +382,9 @@ namespace Barotrauma
var removals = new List<WayPoint>();
WayPoint previous = null;
float tooClose = outSideWaypointInterval / 2;
foreach (WayPoint wp in outsideWaypoints)
foreach (var wayPoint in outsideWaypoints)
{
WayPoint wp = wayPoint.Item1;
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)
{
@@ -341,8 +393,9 @@ namespace Barotrauma
previous = wp;
continue;
}
foreach (WayPoint otherWp in outsideWaypoints)
foreach (var otherWayPoint in outsideWaypoints)
{
WayPoint otherWp = otherWayPoint.Item1;
if (otherWp == wp) { continue; }
if (removals.Contains(otherWp)) { continue; }
float sqrDist = Vector2.DistanceSquared(wp.Position, otherWp.Position);
@@ -356,13 +409,12 @@ namespace Barotrauma
}
foreach (WayPoint wp in removals)
{
outsideWaypoints.Remove(wp);
outsideWaypoints.RemoveAll(w => w.Item1 == 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];
WayPoint current = outsideWaypoints[i].Item1;
if (current.linkedTo.Count > 1) { continue; }
WayPoint next = null;
int maxConnections = 2;
@@ -371,19 +423,12 @@ namespace Barotrauma
{
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)));
next = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp.Item1 != next && wp.Item1.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.Item1.linkedTo.Count < 2 && wp.Item2 < i);
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.");
}
}
}
@@ -532,7 +577,7 @@ namespace Barotrauma
}
else
{
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 70), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 100), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
}
if (closest == null) { continue; }
ladderPoint.ConnectTo(closest);
@@ -601,13 +646,20 @@ namespace Barotrauma
}
}
var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && !w.linkedTo.Any());
var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && w.linkedTo.None());
foreach (WayPoint wp in orphans)
{
wp.Remove();
}
foreach (WayPoint wp in WayPointList)
{
if (wp.CurrentHull == null && wp.Ladders == null && wp.linkedTo.Count < 2)
{
DebugConsole.ThrowError($"Couldn't automatically link the waypoint {wp.ID} outside of the submarine. You should do it manually. The waypoint ID is shown in red color.");
}
}
//re-disable the bodies of the doors that are supposed to be open
foreach (Door door in openDoors)
{
@@ -617,18 +669,20 @@ namespace Barotrauma
return true;
}
private WayPoint FindClosestOutside(IEnumerable<WayPoint> waypointList, float tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<WayPoint, bool> filter = null)
private WayPoint FindClosestOutside(IEnumerable<(WayPoint, int)> waypointList, float tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<(WayPoint, int), bool> filter = null)
{
float closestDist = 0;
WayPoint closest = null;
foreach (WayPoint wp in waypointList)
foreach (var wayPoint in waypointList)
{
WayPoint wp = wayPoint.Item1;
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; }
if (filter != null && !filter(wayPoint)) { continue; }
float sqrDist = Vector2.DistanceSquared(Position, wp.Position);
if (sqrDist > tolerance * tolerance) { continue; }
if (closest == null || sqrDist < closestDist)
{
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);