Build 1.1.4.0
This commit is contained in:
@@ -16,6 +16,11 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Level : Entity, IServerSerializable
|
||||
{
|
||||
public enum PlacementType
|
||||
{
|
||||
Top, Bottom
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
{
|
||||
SingleDestructibleWall,
|
||||
@@ -61,6 +66,7 @@ namespace Barotrauma
|
||||
[Flags]
|
||||
public enum PositionType
|
||||
{
|
||||
None = 0,
|
||||
MainPath = 0x1,
|
||||
SidePath = 0x2,
|
||||
Cave = 0x4,
|
||||
@@ -68,7 +74,8 @@ namespace Barotrauma
|
||||
Wreck = 0x10,
|
||||
BeaconStation = 0x20,
|
||||
Abyss = 0x40,
|
||||
AbyssCave = 0x80
|
||||
AbyssCave = 0x80,
|
||||
Outpost = 0x100,
|
||||
}
|
||||
|
||||
public struct InterestingPosition
|
||||
@@ -413,6 +420,9 @@ namespace Barotrauma
|
||||
get { return LevelData.Type; }
|
||||
}
|
||||
|
||||
|
||||
public bool IsEndBiome => LevelData.Biome != null && LevelData.Biome.IsEndBiome;
|
||||
|
||||
/// <summary>
|
||||
/// Is there a loaded level set and is it an outpost?
|
||||
/// </summary>
|
||||
@@ -451,6 +461,19 @@ namespace Barotrauma
|
||||
borders = new Rectangle(Point.Zero, levelData.Size);
|
||||
}
|
||||
|
||||
public bool ShouldSpawnCrewInsideOutpost()
|
||||
{
|
||||
if (StartOutpost != null &&
|
||||
Type == LevelData.LevelType.Outpost &&
|
||||
(StartOutpost.Info.OutpostGenerationParams?.SpawnCrewInsideOutpost ?? false) &&
|
||||
StartOutpost.GetConnectedSubs().Any(s => s.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
var reputation = GameMain.GameSession?.Campaign?.Map?.CurrentLocation?.Reputation;
|
||||
return reputation == null || reputation.NormalizedValue >= Reputation.HostileThreshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Level Generate(LevelData levelData, bool mirror, Location startLocation, Location endLocation, SubmarineInfo startOutpost = null, SubmarineInfo endOutpost = null)
|
||||
{
|
||||
Debug.Assert(levelData.Biome != null);
|
||||
@@ -482,11 +505,8 @@ namespace Barotrauma
|
||||
EntitiesBeforeGenerate = GetEntities().ToList();
|
||||
EntityCountBeforeGenerate = EntitiesBeforeGenerate.Count();
|
||||
|
||||
if (LevelData.ForceOutpostGenerationParams == null)
|
||||
{
|
||||
StartLocation = startLocation;
|
||||
EndLocation = endLocation;
|
||||
}
|
||||
StartLocation = startLocation;
|
||||
EndLocation = endLocation;
|
||||
|
||||
GenerateEqualityCheckValue(LevelGenStage.GenStart);
|
||||
SetEqualityCheckValue(LevelGenStage.LevelGenParams, unchecked((int)GenerationParams.UintIdentifier));
|
||||
@@ -889,6 +909,12 @@ namespace Barotrauma
|
||||
// remove unnecessary cells and create some holes at the bottom of the level
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
if (GenerationParams.NoLevelGeometry)
|
||||
{
|
||||
cells.ForEach(c => c.CellType = CellType.Removed);
|
||||
cells.Clear();
|
||||
}
|
||||
|
||||
cells = cells.Except(pathCells).ToList();
|
||||
//remove cells from the edges and bottom of the map because a clean-cut edge of the level looks bad
|
||||
cells.ForEachMod(c =>
|
||||
@@ -1687,14 +1713,22 @@ namespace Barotrauma
|
||||
foreach (VoronoiCell cell in closeCells)
|
||||
{
|
||||
bool tooClose = false;
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
if (Vector2.DistanceSquared(edge.Point1, position) < minDistSqr ||
|
||||
Vector2.DistanceSquared(edge.Point2, position) < minDistSqr ||
|
||||
MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), position.ToPoint()) < minDistSqr)
|
||||
|
||||
if (cell.IsPointInsideAABB(position, margin: minDistance))
|
||||
{
|
||||
tooClose = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
tooClose = true;
|
||||
break;
|
||||
if (Vector2.DistanceSquared(edge.Point1, position) < minDistSqr ||
|
||||
Vector2.DistanceSquared(edge.Point2, position) < minDistSqr ||
|
||||
MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), position.ToPoint()) < minDistSqr)
|
||||
{
|
||||
tooClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tooClose) { tooCloseCells.Add(cell); }
|
||||
@@ -1779,6 +1813,7 @@ namespace Barotrauma
|
||||
|
||||
if (AbyssArea.Height < islandSize.Y) { return; }
|
||||
|
||||
int createdCaves = 0;
|
||||
int islandCount = GenerationParams.AbyssIslandCount;
|
||||
for (int i = 0; i < islandCount; i++)
|
||||
{
|
||||
@@ -1808,7 +1843,11 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) > GenerationParams.AbyssIslandCaveProbability)
|
||||
bool createCave =
|
||||
//force at least one abyss cave
|
||||
(i == islandCount - 1 && createdCaves == 0) ||
|
||||
Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) > GenerationParams.AbyssIslandCaveProbability;
|
||||
if (!createCave)
|
||||
{
|
||||
float radiusVariance = Math.Min(islandArea.Width, islandArea.Height) * 0.1f;
|
||||
var vertices = CaveGenerator.CreateRandomChunk(islandArea.Width - (int)(radiusVariance * 2), islandArea.Height - (int)(radiusVariance * 2), 16, radiusVariance: radiusVariance);
|
||||
@@ -1867,6 +1906,7 @@ namespace Barotrauma
|
||||
new Point(islandArea.Center.X, islandArea.Center.Y + (int)(islandArea.Size.Y * (1.0f - caveScaleRelativeToIsland)) / 2),
|
||||
new Point((int)(islandArea.Size.X * caveScaleRelativeToIsland), (int)(islandArea.Size.Y * caveScaleRelativeToIsland)));
|
||||
AbyssIslands.Add(new AbyssIsland(islandArea, islandCells));
|
||||
createdCaves++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2942,13 +2982,13 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="rotation">Used by clients to set the rotation for the resources</param>
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, out float rotation, IEnumerable<Cave> targetCaves = null)
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, IEnumerable<Cave> targetCaves = null)
|
||||
{
|
||||
var allValidLocations = GetAllValidClusterLocations();
|
||||
var placedResources = new List<Item>();
|
||||
rotation = 0.0f;
|
||||
|
||||
if (allValidLocations.None()) { return placedResources; } // TODO: WHAT?!
|
||||
// if there are no valid locations, don't place anything
|
||||
if (allValidLocations.None()) { return placedResources; }
|
||||
|
||||
// Make sure not to pick a spot that already has other level resources
|
||||
for (int i = allValidLocations.Count - 1; i >= 0; i--)
|
||||
@@ -2979,10 +3019,12 @@ namespace Barotrauma
|
||||
|
||||
if (PositionsOfInterest.None(p => p.PositionType == positionType))
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to find a position of the type \"{positionType}\" for mission resources.");
|
||||
foreach (var validType in MineralMission.ValidPositionTypes)
|
||||
{
|
||||
if (validType != positionType && PositionsOfInterest.Any(p => p.PositionType == validType))
|
||||
{
|
||||
DebugConsole.AddWarning($"Placing in \"{validType}\" instead.");
|
||||
positionType = validType;
|
||||
break;
|
||||
}
|
||||
@@ -3042,7 +3084,6 @@ namespace Barotrauma
|
||||
}
|
||||
PlaceResources(prefab, requiredAmount, selectedLocation, out placedResources);
|
||||
Vector2 edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
|
||||
rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
|
||||
return placedResources;
|
||||
|
||||
static bool IsOnMainPath(ClusterLocation location) => location.Edge.NextToMainPath;
|
||||
@@ -3151,9 +3192,7 @@ namespace Barotrauma
|
||||
if (item.GetComponent<Holdable>() is Holdable h)
|
||||
{
|
||||
h.AttachToWall();
|
||||
#if CLIENT
|
||||
item.Rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
|
||||
#endif
|
||||
}
|
||||
else if (item.body != null)
|
||||
{
|
||||
@@ -3234,7 +3273,8 @@ namespace Barotrauma
|
||||
{
|
||||
suitablePositions.RemoveAll(p => !filter(p));
|
||||
}
|
||||
if (positionType.HasFlag(PositionType.MainPath) || positionType.HasFlag(PositionType.SidePath))
|
||||
if (positionType.HasFlag(PositionType.MainPath) || positionType.HasFlag(PositionType.SidePath) || positionType.HasFlag(PositionType.Abyss) ||
|
||||
positionType.HasFlag(PositionType.Cave) || positionType.HasFlag(PositionType.AbyssCave))
|
||||
{
|
||||
suitablePositions.RemoveAll(p => IsPositionInsideWall(p.Position.ToVector2()));
|
||||
}
|
||||
@@ -3399,8 +3439,7 @@ namespace Barotrauma
|
||||
bool closeEnough = false;
|
||||
foreach (VoronoiCell cell in wall.Cells)
|
||||
{
|
||||
if (Math.Abs(cell.Center.X - worldPos.X) < (searchDepth + 1) * GridCellSize &&
|
||||
Math.Abs(cell.Center.Y - worldPos.Y) < (searchDepth + 1) * GridCellSize)
|
||||
if (cell.IsPointInsideAABB(worldPos, margin: (searchDepth + 1) * GridCellSize / 2))
|
||||
{
|
||||
closeEnough = true;
|
||||
break;
|
||||
@@ -3553,6 +3592,13 @@ namespace Barotrauma
|
||||
|
||||
var subDoc = SubmarineInfo.OpenFile(contentFile.Path.Value);
|
||||
Rectangle subBorders = Submarine.GetBorders(subDoc.Root);
|
||||
SubmarineInfo info = new SubmarineInfo(contentFile.Path.Value)
|
||||
{
|
||||
Type = type
|
||||
};
|
||||
|
||||
//place downwards by default
|
||||
var placement = info.BeaconStationInfo?.Placement ?? PlacementType.Bottom;
|
||||
|
||||
// Add some margin so that the sub doesn't block the path entirely. It's still possible that some larger subs can't pass by.
|
||||
Point paddedDimensions = new Point(subBorders.Width + 3000, subBorders.Height + 3000);
|
||||
@@ -3573,7 +3619,7 @@ namespace Barotrauma
|
||||
attemptsLeft--;
|
||||
if (TryGetSpawnPoint(out spawnPoint))
|
||||
{
|
||||
success = TryPositionSub(subBorders, subName, ref spawnPoint);
|
||||
success = TryPositionSub(subBorders, subName, placement, ref spawnPoint);
|
||||
if (success)
|
||||
{
|
||||
break;
|
||||
@@ -3594,10 +3640,6 @@ namespace Barotrauma
|
||||
{
|
||||
Debug.WriteLine($"Sub {subName} successfully positioned to {spawnPoint} in {tempSW.ElapsedMilliseconds} (ms)");
|
||||
tempSW.Restart();
|
||||
SubmarineInfo info = new SubmarineInfo(contentFile.Path.Value)
|
||||
{
|
||||
Type = type
|
||||
};
|
||||
Submarine sub = new Submarine(info);
|
||||
if (type == SubmarineType.Wreck)
|
||||
{
|
||||
@@ -3647,10 +3689,10 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
bool TryPositionSub(Rectangle subBorders, string subName, ref Vector2 spawnPoint)
|
||||
{
|
||||
bool TryPositionSub(Rectangle subBorders, string subName, PlacementType placement, ref Vector2 spawnPoint)
|
||||
{
|
||||
positions.Add(spawnPoint);
|
||||
bool bottomFound = TryRaycastToBottom(subBorders, ref spawnPoint);
|
||||
bool bottomFound = TryRaycast(subBorders, placement, ref spawnPoint);
|
||||
positions.Add(spawnPoint);
|
||||
|
||||
bool leftSideBlocked = IsSideBlocked(subBorders, false);
|
||||
@@ -3658,21 +3700,21 @@ namespace Barotrauma
|
||||
int step = 5;
|
||||
if (rightSideBlocked && !leftSideBlocked)
|
||||
{
|
||||
bottomFound = TryMove(subBorders, ref spawnPoint, -step);
|
||||
bottomFound = TryMove(subBorders, placement, ref spawnPoint, -step);
|
||||
}
|
||||
else if (leftSideBlocked && !rightSideBlocked)
|
||||
{
|
||||
bottomFound = TryMove(subBorders, ref spawnPoint, step);
|
||||
bottomFound = TryMove(subBorders, placement, ref spawnPoint, step);
|
||||
}
|
||||
else if (!bottomFound)
|
||||
{
|
||||
if (!leftSideBlocked)
|
||||
{
|
||||
bottomFound = TryMove(subBorders, ref spawnPoint, -step);
|
||||
bottomFound = TryMove(subBorders, placement, ref spawnPoint, -step);
|
||||
}
|
||||
else if (!rightSideBlocked)
|
||||
{
|
||||
bottomFound = TryMove(subBorders, ref spawnPoint, step);
|
||||
bottomFound = TryMove(subBorders, placement, ref spawnPoint, step);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3702,14 +3744,14 @@ namespace Barotrauma
|
||||
}
|
||||
return !isBlocked && bottomFound;
|
||||
|
||||
bool TryMove(Rectangle subBorders, ref Vector2 spawnPoint, float amount)
|
||||
bool TryMove(Rectangle subBorders, PlacementType placement, ref Vector2 spawnPoint, float amount)
|
||||
{
|
||||
float maxMovement = 5000;
|
||||
float totalAmount = 0;
|
||||
bool foundBottom = TryRaycastToBottom(subBorders, ref spawnPoint);
|
||||
bool foundBottom = TryRaycast(subBorders, placement, ref spawnPoint);
|
||||
while (!IsSideBlocked(subBorders, amount > 0))
|
||||
{
|
||||
foundBottom = TryRaycastToBottom(subBorders, ref spawnPoint);
|
||||
foundBottom = TryRaycast(subBorders, placement, ref spawnPoint);
|
||||
totalAmount += amount;
|
||||
spawnPoint = new Vector2(spawnPoint.X + amount, spawnPoint.Y);
|
||||
if (Math.Abs(totalAmount) > maxMovement)
|
||||
@@ -3738,7 +3780,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryRaycastToBottom(Rectangle subBorders, ref Vector2 spawnPoint)
|
||||
bool TryRaycast(Rectangle subBorders, PlacementType placement, ref Vector2 spawnPoint)
|
||||
{
|
||||
// Shoot five rays and pick the highest hit point.
|
||||
int rayCount = 5;
|
||||
@@ -3764,16 +3806,18 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
var simPos = ConvertUnits.ToSimUnits(rayStart);
|
||||
var body = Submarine.PickBody(simPos, new Vector2(simPos.X, -1),
|
||||
customPredicate: f => f.Body?.UserData is VoronoiCell cell && cell.Body.BodyType == BodyType.Static && !ExtraWalls.Any(w => w.Body == f.Body),
|
||||
var body = Submarine.PickBody(simPos, new Vector2(simPos.X, placement == PlacementType.Bottom ? -1 : Size.Y + 1),
|
||||
customPredicate: f => f.Body == TopBarrier || f.Body == BottomBarrier || (f.Body?.UserData is VoronoiCell cell && cell.Body.BodyType == BodyType.Static && !ExtraWalls.Any(w => w.Body == f.Body)),
|
||||
collisionCategory: Physics.CollisionLevel | Physics.CollisionWall);
|
||||
if (body != null)
|
||||
{
|
||||
positions[i] = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + new Vector2(0, subBorders.Height / 2);
|
||||
positions[i] =
|
||||
ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) +
|
||||
new Vector2(0, subBorders.Height / 2 * (placement == PlacementType.Bottom ? 1 : -1));
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
float highestPoint = positions.Max(p => p.Y);
|
||||
float highestPoint = placement == PlacementType.Bottom ? positions.Max(p => p.Y) : positions.Min(p => p.Y);
|
||||
spawnPoint = new Vector2(spawnPoint.X, highestPoint);
|
||||
return hit;
|
||||
}
|
||||
@@ -3953,8 +3997,18 @@ namespace Barotrauma
|
||||
if (LevelData.OutpostGenerationParamsExist)
|
||||
{
|
||||
Location location = i == 0 ? StartLocation : EndLocation;
|
||||
OutpostGenerationParams outpostGenerationParams = LevelData.ForceOutpostGenerationParams ??
|
||||
LevelData.GetSuitableOutpostGenerationParams(location).GetRandom(Rand.RandSync.ServerAndClient);
|
||||
OutpostGenerationParams outpostGenerationParams = null;
|
||||
if (LevelData.ForceOutpostGenerationParams != null)
|
||||
{
|
||||
outpostGenerationParams = LevelData.ForceOutpostGenerationParams;
|
||||
}
|
||||
else
|
||||
{
|
||||
outpostGenerationParams =
|
||||
LevelData.ForceOutpostGenerationParams ??
|
||||
LevelData.GetSuitableOutpostGenerationParams(location, LevelData).GetRandom(Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
LocationType locationType = location?.Type;
|
||||
if (locationType == null)
|
||||
{
|
||||
@@ -4030,52 +4084,70 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
DockingPort outpostPort = null;
|
||||
closestDistance = float.MaxValue;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
Vector2 spawnPos;
|
||||
if (GenerationParams.ForceOutpostPosition != Vector2.Zero)
|
||||
{
|
||||
if (port.IsHorizontal || port.Docked) { continue; }
|
||||
if (port.Item.Submarine != outpost) { continue; }
|
||||
//the outpost port has to be at the bottom of the outpost
|
||||
if (port.Item.WorldPosition.Y > outpost.WorldPosition.Y) { continue; }
|
||||
float dist = Math.Abs(port.Item.WorldPosition.X - outpost.WorldPosition.X);
|
||||
if (dist < closestDistance)
|
||||
spawnPos = new Vector2(Size.X * GenerationParams.ForceOutpostPosition.X, Size.Y * GenerationParams.ForceOutpostPosition.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
DockingPort outpostPort = null;
|
||||
closestDistance = float.MaxValue;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
{
|
||||
outpostPort = port;
|
||||
closestDistance = dist;
|
||||
if (port.IsHorizontal || port.Docked) { continue; }
|
||||
if (port.Item.Submarine != outpost) { continue; }
|
||||
//the outpost port has to be at the bottom of the outpost
|
||||
if (port.Item.WorldPosition.Y > outpost.WorldPosition.Y) { continue; }
|
||||
float dist = Math.Abs(port.Item.WorldPosition.X - outpost.WorldPosition.X);
|
||||
if (dist < closestDistance)
|
||||
{
|
||||
outpostPort = 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) > 5000.0f)
|
||||
{
|
||||
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Info.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.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
|
||||
float? outpostDockingPortOffset = null;
|
||||
if (outpostPort != null)
|
||||
{
|
||||
outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset.Value) > 5000.0f)
|
||||
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) > 5000.0f)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset.Value, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
|
||||
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Info.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:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
|
||||
float? outpostDockingPortOffset = null;
|
||||
if (outpostPort != null)
|
||||
{
|
||||
outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset.Value) > 5000.0f)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset.Value, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). 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:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
}
|
||||
|
||||
spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, outpostDockingPortOffset != null ? subDockingPortOffset - outpostDockingPortOffset.Value : 0.0f, verticalMoveDir: 1);
|
||||
if (Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
spawnPos.Y = Math.Min(Size.Y - outpost.Borders.Height * 0.6f, spawnPos.Y + outpost.Borders.Height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, outpostDockingPortOffset != null ? subDockingPortOffset - outpostDockingPortOffset.Value : 0.0f, verticalMoveDir: 1);
|
||||
if (Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
spawnPos.Y = Math.Min(Size.Y - outpost.Borders.Height * 0.6f, spawnPos.Y + outpost.Borders.Height / 2);
|
||||
}
|
||||
outpost.SetPosition(spawnPos, forceUndockFromStaticSubmarines: false);
|
||||
|
||||
foreach (WayPoint wp in WayPoint.WayPointList)
|
||||
{
|
||||
if (wp.Submarine == outpost && wp.SpawnType != SpawnType.Path)
|
||||
{
|
||||
PositionsOfInterest.Add(new InterestingPosition(wp.WorldPosition.ToPoint(), PositionType.Outpost, outpost));
|
||||
}
|
||||
}
|
||||
|
||||
if ((i == 0) == !Mirrored)
|
||||
{
|
||||
StartOutpost = outpost;
|
||||
@@ -4094,13 +4166,12 @@ namespace Barotrauma
|
||||
outpost.Info.Name = EndLocation.Name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBeaconStation()
|
||||
{
|
||||
if (!LevelData.HasBeaconStation) { return; }
|
||||
if (!LevelData.HasBeaconStation && string.IsNullOrEmpty(GenerationParams.ForceBeaconStation)) { return; }
|
||||
var beaconStationFiles = ContentPackageManager.EnabledPackages.All
|
||||
.SelectMany(p => p.GetFiles<BeaconStationFile>())
|
||||
.OrderBy(f => f.UintIdentifier).ToList();
|
||||
@@ -4111,27 +4182,40 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var beaconInfos = SubmarineInfo.SavedSubmarines.Where(i => i.IsBeacon);
|
||||
for (int i = beaconStationFiles.Count - 1; i >= 0; i--)
|
||||
ContentFile contentFile = null;
|
||||
if (!string.IsNullOrEmpty(GenerationParams.ForceBeaconStation))
|
||||
{
|
||||
var beaconStationFile = beaconStationFiles[i];
|
||||
var matchingInfo = beaconInfos.SingleOrDefault(info => info.FilePath == beaconStationFile.Path.Value);
|
||||
Debug.Assert(matchingInfo != null);
|
||||
if (matchingInfo?.BeaconStationInfo is BeaconStationInfo beaconInfo)
|
||||
contentFile = beaconStationFiles.FirstOrDefault(f => f.Path == GenerationParams.ForceBeaconStation);
|
||||
if (contentFile == null)
|
||||
{
|
||||
if (LevelData.Difficulty < beaconInfo.MinLevelDifficulty || LevelData.Difficulty > beaconInfo.MaxLevelDifficulty)
|
||||
{
|
||||
beaconStationFiles.RemoveAt(i);
|
||||
}
|
||||
DebugConsole.ThrowError($"Failed to find the beacon station \"{GenerationParams.ForceBeaconStation}\". Using a random one instead...");
|
||||
}
|
||||
}
|
||||
if (beaconStationFiles.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"No BeaconStation files found for the level difficulty {LevelData.Difficulty}!");
|
||||
return;
|
||||
}
|
||||
var contentFile = beaconStationFiles.GetRandom(Rand.RandSync.ServerAndClient);
|
||||
string beaconStationName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path.Value);
|
||||
|
||||
if (contentFile == null)
|
||||
{
|
||||
for (int i = beaconStationFiles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var beaconStationFile = beaconStationFiles[i];
|
||||
var matchingInfo = beaconInfos.SingleOrDefault(info => info.FilePath == beaconStationFile.Path.Value);
|
||||
Debug.Assert(matchingInfo != null);
|
||||
if (matchingInfo?.BeaconStationInfo is BeaconStationInfo beaconInfo)
|
||||
{
|
||||
if (LevelData.Difficulty < beaconInfo.MinLevelDifficulty || LevelData.Difficulty > beaconInfo.MaxLevelDifficulty)
|
||||
{
|
||||
beaconStationFiles.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (beaconStationFiles.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"No BeaconStation files found for the level difficulty {LevelData.Difficulty}!");
|
||||
return;
|
||||
}
|
||||
contentFile = beaconStationFiles.GetRandom(Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
string beaconStationName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path.Value);
|
||||
BeaconStation = SpawnSubOnPath(beaconStationName, contentFile, SubmarineType.BeaconStation);
|
||||
if (BeaconStation == null)
|
||||
{
|
||||
@@ -4195,7 +4279,7 @@ namespace Barotrauma
|
||||
{
|
||||
bool allowDisconnectedWires = true;
|
||||
bool allowDamagedWalls = true;
|
||||
if (BeaconStation.Info?.BeaconStationInfo is BeaconStationInfo info)
|
||||
if (BeaconStation?.Info?.BeaconStationInfo is BeaconStationInfo info)
|
||||
{
|
||||
allowDisconnectedWires = info.AllowDisconnectedWires;
|
||||
allowDamagedWalls = info.AllowDamagedWalls;
|
||||
@@ -4346,7 +4430,7 @@ namespace Barotrauma
|
||||
corpse.AnimController.FindHull(worldPos, setSubmarine: true);
|
||||
corpse.TeamID = CharacterTeamType.None;
|
||||
corpse.EnableDespawn = false;
|
||||
selectedPrefab.GiveItems(corpse, wreck);
|
||||
selectedPrefab.GiveItems(corpse, wreck, sp);
|
||||
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
|
||||
corpse.CharacterHealth.ApplyAffliction(corpse.AnimController.MainLimb, AfflictionPrefab.OxygenLow.Instantiate(200));
|
||||
bool applyBurns = Rand.Value() < 0.1f;
|
||||
@@ -4377,7 +4461,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
corpse.CharacterHealth.ForceUpdateVisuals();
|
||||
corpse.GiveIdCardTags(sp);
|
||||
|
||||
bool isServerOrSingleplayer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true };
|
||||
if (isServerOrSingleplayer && selectedPrefab.MinMoney >= 0 && selectedPrefab.MaxMoney > 0)
|
||||
|
||||
Reference in New Issue
Block a user