v0.12.0.2
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -135,6 +136,13 @@ namespace Barotrauma
|
||||
case "walledge":
|
||||
WallEdgeSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "overridecommonness":
|
||||
string levelType = subElement.GetAttributeString("leveltype", "").ToLowerInvariant();
|
||||
if (!OverrideCommonness.ContainsKey(levelType))
|
||||
{
|
||||
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,5 +201,30 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(XElement element)
|
||||
{
|
||||
SerializableProperty.SerializeProperties(this, element, true);
|
||||
foreach (KeyValuePair<string, float> overrideCommonness in OverrideCommonness)
|
||||
{
|
||||
bool elementFound = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("overridecommonness", StringComparison.OrdinalIgnoreCase)
|
||||
&& subElement.GetAttributeString("leveltype", "").Equals(overrideCommonness.Key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture);
|
||||
elementFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!elementFound)
|
||||
{
|
||||
element.Add(new XElement("overridecommonness",
|
||||
new XAttribute("leveltype", overrideCommonness.Key),
|
||||
new XAttribute("commonness", overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,14 +169,16 @@ namespace Barotrauma
|
||||
sw2.Start();
|
||||
|
||||
List<VoronoiCell> pathCells = new List<VoronoiCell>();
|
||||
|
||||
|
||||
if (targetCells.Count == 0) { return pathCells; }
|
||||
|
||||
VoronoiCell currentCell = targetCells[0];
|
||||
currentCell.CellType = CellType.Path;
|
||||
pathCells.Add(currentCell);
|
||||
|
||||
int currentTargetIndex = 0;
|
||||
|
||||
int iterationsLeft = cells.Count;
|
||||
int iterationsLeft = cells.Count / 2;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -189,10 +191,14 @@ namespace Barotrauma
|
||||
if (adjacentCell == null) { continue; }
|
||||
double dist = MathUtils.Distance(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y, targetCells[currentTargetIndex].Site.Coord.X, targetCells[currentTargetIndex].Site.Coord.Y);
|
||||
dist += MathUtils.Distance(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y, currentCell.Site.Coord.X, currentCell.Site.Coord.Y) * 0.5f;
|
||||
//disfavor small edges to prevent generating a very small passage
|
||||
if (Vector2.Distance(currentCell.Edges[i].Point1, currentCell.Edges[i].Point2) < 200.0f)
|
||||
|
||||
//disfavor short edges to prevent generating a very small passage
|
||||
if (Vector2.DistanceSquared(currentCell.Edges[i].Point1, currentCell.Edges[i].Point2) < 150.0f * 150.0f)
|
||||
{
|
||||
dist += 1000000;
|
||||
//divide by the number of times the current cell has been used
|
||||
// prevents the path from getting "stuck" (jumping back and forth between adjacent cells)
|
||||
// if there's no other way to the destination than going through a short edge
|
||||
dist *= 10.0f / Math.Max(pathCells.Count(c => c == currentCell), 1.0f);
|
||||
}
|
||||
if (dist < smallestDist)
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Barotrauma
|
||||
public DestructibleLevelWall(List<Vector2> vertices, Color color, Level level, float? health = null, bool giftWrap = false)
|
||||
: base (vertices, color, level, giftWrap)
|
||||
{
|
||||
MaxHealth = health ?? MathHelper.Clamp(Body.Mass, 100.0f, 1000.0f);
|
||||
MaxHealth = health ?? MathHelper.Clamp(Body.Mass * 0.5f, 50.0f, 1000.0f);
|
||||
Cells.ForEach(c => c.IsDestructible = true);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,14 +45,35 @@ namespace Barotrauma
|
||||
public bool IsValid;
|
||||
public Submarine Submarine;
|
||||
public Ruin Ruin;
|
||||
public Cave Cave;
|
||||
|
||||
public InterestingPosition(Point position, PositionType positionType, bool isValid = true, Submarine submarine = null, Ruin ruin = null)
|
||||
public InterestingPosition(Point position, PositionType positionType, Submarine submarine = null, bool isValid = true)
|
||||
{
|
||||
Position = position;
|
||||
PositionType = positionType;
|
||||
IsValid = isValid;
|
||||
Submarine = submarine;
|
||||
Ruin = null;
|
||||
Cave = null;
|
||||
}
|
||||
|
||||
public InterestingPosition(Point position, PositionType positionType, Ruin ruin, bool isValid = true)
|
||||
{
|
||||
Position = position;
|
||||
PositionType = positionType;
|
||||
IsValid = isValid;
|
||||
Submarine = null;
|
||||
Ruin = ruin;
|
||||
Cave = null;
|
||||
}
|
||||
public InterestingPosition(Point position, PositionType positionType, Cave cave, bool isValid = true)
|
||||
{
|
||||
Position = position;
|
||||
PositionType = positionType;
|
||||
IsValid = isValid;
|
||||
Submarine = null;
|
||||
Ruin = null;
|
||||
Cave = cave;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +127,8 @@ namespace Barotrauma
|
||||
|
||||
public Point StartPos, EndPos;
|
||||
|
||||
public bool DisplayOnSonar;
|
||||
|
||||
public readonly CaveGenerationParams CaveGenerationParams;
|
||||
|
||||
public Cave(CaveGenerationParams caveGenerationParams, Rectangle area, Point startPos, Point endPos)
|
||||
@@ -375,6 +398,7 @@ namespace Barotrauma
|
||||
minWidth = Math.Min(minWidth, MaxSubmarineWidth);
|
||||
}
|
||||
minWidth = Math.Min(minWidth, borders.Width / 5);
|
||||
LevelData.MinMainPathWidth = minWidth;
|
||||
|
||||
Rectangle pathBorders = borders;
|
||||
pathBorders.Inflate(
|
||||
@@ -435,6 +459,7 @@ namespace Barotrauma
|
||||
Point siteVariance = GenerationParams.VoronoiSiteVariance;
|
||||
siteCoordsX = new List<double>((borders.Height / siteInterval.Y) * (borders.Width / siteInterval.Y));
|
||||
siteCoordsY = new List<double>((borders.Height / siteInterval.Y) * (borders.Width / siteInterval.Y));
|
||||
int caveSiteInterval = 500;
|
||||
for (int x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X)
|
||||
{
|
||||
for (int y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y)
|
||||
@@ -448,7 +473,7 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 1; i < tunnel.Nodes.Count; i++)
|
||||
{
|
||||
float minDist = Math.Max(tunnel.MinWidth, Math.Max(siteInterval.X, siteInterval.Y)) * 2.0f;
|
||||
float minDist = Math.Max(tunnel.MinWidth * 2.0f, Math.Max(siteInterval.X, siteInterval.Y));
|
||||
if (siteX < Math.Min(tunnel.Nodes[i - 1].X, tunnel.Nodes[i].X) - minDist) { continue; }
|
||||
if (siteX > Math.Max(tunnel.Nodes[i - 1].X, tunnel.Nodes[i].X) + minDist) { continue; }
|
||||
if (siteY < Math.Min(tunnel.Nodes[i - 1].Y, tunnel.Nodes[i].Y) - minDist) { continue; }
|
||||
@@ -459,7 +484,7 @@ namespace Barotrauma
|
||||
{
|
||||
closeToTunnel = true;
|
||||
tunnelDistSqr = MathUtils.LineSegmentToPointDistanceSquared(tunnel.Nodes[i - 1], tunnel.Nodes[i], new Point(siteX, siteY));
|
||||
if (tunnel.Type == TunnelType.Cave )
|
||||
if (tunnel.Type == TunnelType.Cave)
|
||||
{
|
||||
closeToCave = true;
|
||||
}
|
||||
@@ -474,31 +499,64 @@ namespace Barotrauma
|
||||
if (Rand.Range(0, 10, Rand.RandSync.Server) != 0) { continue; }
|
||||
}
|
||||
|
||||
if (closeToCave)
|
||||
{
|
||||
//add some more sites around caves to generate more small voronoi cells
|
||||
if (x < borders.Width - siteInterval.X)
|
||||
{
|
||||
siteCoordsX.Add(x + Rand.Range(siteInterval.X / 4, siteInterval.X / 2, Rand.RandSync.Server));
|
||||
siteCoordsY.Add(y);
|
||||
}
|
||||
if (y < borders.Height - siteInterval.Y)
|
||||
{
|
||||
siteCoordsX.Add(x);
|
||||
siteCoordsY.Add(y + Rand.Range(siteInterval.Y / 4, siteInterval.Y / 2, Rand.RandSync.Server));
|
||||
}
|
||||
if (x < borders.Width - siteInterval.X && y < borders.Height - siteInterval.Y)
|
||||
{
|
||||
siteCoordsX.Add(x + Rand.Range(siteInterval.X / 4, siteInterval.X / 2, Rand.RandSync.Server));
|
||||
siteCoordsY.Add(y + Rand.Range(siteInterval.Y / 4, siteInterval.Y / 2, Rand.RandSync.Server));
|
||||
}
|
||||
}
|
||||
|
||||
siteCoordsX.Add(siteX);
|
||||
siteCoordsY.Add(siteY);
|
||||
|
||||
if (closeToCave)
|
||||
{
|
||||
for (int x2 = x; x2 < x + siteInterval.X; x2 += caveSiteInterval)
|
||||
{
|
||||
for (int y2 = y; y2 < y + siteInterval.Y; y2 += caveSiteInterval)
|
||||
{
|
||||
int caveSiteX = x2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.Server);
|
||||
int caveSiteY = y2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.Server);
|
||||
|
||||
bool tooClose = false;
|
||||
for (int i = 0; i < siteCoordsX.Count; i++)
|
||||
{
|
||||
if (MathUtils.DistanceSquared(caveSiteX, caveSiteY, siteCoordsX[i], siteCoordsY[i]) < 10.0f * 10.0f)
|
||||
{
|
||||
tooClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tooClose) { continue; }
|
||||
siteCoordsX.Add(caveSiteX);
|
||||
siteCoordsY.Add(caveSiteY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*int caveSiteInterval = 500;
|
||||
foreach (Cave cave in Caves)
|
||||
{
|
||||
for (int x = cave.Area.X; x < cave.Area.Right; x += caveSiteInterval)
|
||||
{
|
||||
for (int y = cave.Area.Y; y < cave.Area.Bottom; y += caveSiteInterval)
|
||||
{
|
||||
int siteX = x + Rand.Int(caveSiteInterval / 2);
|
||||
int siteY = y + Rand.Int(caveSiteInterval / 2);
|
||||
|
||||
bool tooClose = false;
|
||||
for (int i = 0; i<siteCoordsX.Count; i++)
|
||||
{
|
||||
if (MathUtils.DistanceSquared(siteX, siteY, siteCoordsX[i],siteCoordsY[i]) < 10.0f * 10.0f)
|
||||
{
|
||||
tooClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tooClose) { continue; }
|
||||
siteCoordsX.Add(siteX);
|
||||
siteCoordsY.Add(siteY);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -530,11 +588,13 @@ namespace Barotrauma
|
||||
CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders);
|
||||
if (tunnel.Type == TunnelType.MainPath || tunnel.Type == TunnelType.SidePath)
|
||||
{
|
||||
for (int i = 2; i < tunnel.Cells.Count; i += 3)
|
||||
var distinctCells = tunnel.Cells.Distinct().ToList();
|
||||
for (int i = 2; i < distinctCells.Count; i += 3)
|
||||
{
|
||||
PositionsOfInterest.Add(new InterestingPosition(
|
||||
new Point((int)tunnel.Cells[i].Site.Coord.X, (int)tunnel.Cells[i].Site.Coord.Y),
|
||||
tunnel.Type == TunnelType.MainPath ? PositionType.MainPath : PositionType.SidePath));
|
||||
new Point((int)distinctCells[i].Site.Coord.X, (int)distinctCells[i].Site.Coord.Y),
|
||||
tunnel.Type == TunnelType.MainPath ? PositionType.MainPath : PositionType.SidePath,
|
||||
Caves.Find(cave => cave.Tunnels.Contains(tunnel))));
|
||||
}
|
||||
}
|
||||
GenerateWaypoints(tunnel, parentTunnel: tunnel.ParentTunnel);
|
||||
@@ -703,7 +763,12 @@ namespace Barotrauma
|
||||
{
|
||||
PositionsOfInterest[i] = new InterestingPosition(
|
||||
new Point(borders.Width - PositionsOfInterest[i].Position.X, PositionsOfInterest[i].Position.Y),
|
||||
PositionsOfInterest[i].PositionType);
|
||||
PositionsOfInterest[i].PositionType)
|
||||
{
|
||||
Submarine = PositionsOfInterest[i].Submarine,
|
||||
Cave = PositionsOfInterest[i].Cave,
|
||||
Ruin = PositionsOfInterest[i].Ruin,
|
||||
};
|
||||
}
|
||||
|
||||
foreach (WayPoint waypoint in WayPoint.WayPointList)
|
||||
@@ -726,6 +791,7 @@ namespace Barotrauma
|
||||
cellGrid[x, y].Add(cell);
|
||||
}
|
||||
|
||||
float destructibleWallRatio = MathHelper.Lerp(0.2f, 1.0f, LevelData.Difficulty / 100.0f);
|
||||
foreach (Cave cave in Caves)
|
||||
{
|
||||
CreatePathToClosestTunnel(cave.StartPos);
|
||||
@@ -734,7 +800,7 @@ namespace Barotrauma
|
||||
caveCells.AddRange(cave.Tunnels.SelectMany(t => t.Cells));
|
||||
foreach (var caveCell in caveCells)
|
||||
{
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) < cave.CaveGenerationParams.DestructibleWallRatio)
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) < destructibleWallRatio * cave.CaveGenerationParams.DestructibleWallRatio)
|
||||
{
|
||||
var chunk = CreateIceChunk(caveCell.Edges, caveCell.Center, health: 50.0f);
|
||||
if (chunk != null)
|
||||
@@ -755,7 +821,7 @@ namespace Barotrauma
|
||||
Ruins = new List<Ruin>();
|
||||
for (int i = 0; i < GenerationParams.RuinCount; i++)
|
||||
{
|
||||
GenerateRuin(mainPath.Cells, mirror);
|
||||
GenerateRuin(mainPath, mirror);
|
||||
}
|
||||
|
||||
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
|
||||
@@ -825,25 +891,30 @@ namespace Barotrauma
|
||||
};
|
||||
foreach (Cave cave in Caves)
|
||||
{
|
||||
cellBatches.Add(new Pair<List<VoronoiCell>, Cave>(new List<VoronoiCell>(), cave));
|
||||
var newCellBatch = new Pair<List<VoronoiCell>, Cave>(new List<VoronoiCell>(), cave);
|
||||
foreach (var caveCell in cave.Tunnels.SelectMany(t => t.Cells))
|
||||
{
|
||||
foreach (var edge in caveCell.Edges)
|
||||
{
|
||||
if (!edge.NextToCave) { continue; }
|
||||
if (edge.Cell1?.CellType == CellType.Solid && !cellBatches.Last().First.Contains(edge.Cell1))
|
||||
if (edge.Cell1?.CellType == CellType.Solid && !newCellBatch.First.Contains(edge.Cell1))
|
||||
{
|
||||
cellBatches.First().First.Remove(edge.Cell1);
|
||||
cellBatches.Last().First.Add(edge.Cell1);
|
||||
cellBatches.ForEach(cb => cb.First.Remove(edge.Cell1));
|
||||
newCellBatch.First.Add(edge.Cell1);
|
||||
}
|
||||
if (edge.Cell2?.CellType == CellType.Solid && !cellBatches.Last().First.Contains(edge.Cell2))
|
||||
if (edge.Cell2?.CellType == CellType.Solid && !newCellBatch.First.Contains(edge.Cell2))
|
||||
{
|
||||
cellBatches.First().First.Remove(edge.Cell2);
|
||||
cellBatches.Last().First.Add(edge.Cell2);
|
||||
cellBatches.ForEach(cb => cb.First.Remove(edge.Cell2));
|
||||
newCellBatch.First.Add(edge.Cell2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newCellBatch.First.Any())
|
||||
{
|
||||
cellBatches.Add(newCellBatch);
|
||||
}
|
||||
}
|
||||
cellBatches.RemoveAll(cb => !cb.First.Any());
|
||||
|
||||
Debug.Assert(cellsWithBody.Count == cellBatches.Sum(cb => cb.First.Count));
|
||||
|
||||
@@ -1140,8 +1211,9 @@ namespace Barotrauma
|
||||
|
||||
private void GenerateWaypoints(Tunnel tunnel, Tunnel parentTunnel)
|
||||
{
|
||||
List<WayPoint> wayPoints = new List<WayPoint>();
|
||||
if (tunnel.Cells.Count == 0) { return; }
|
||||
|
||||
List<WayPoint> wayPoints = new List<WayPoint>();
|
||||
for (int i = 0; i < tunnel.Cells.Count; i++)
|
||||
{
|
||||
tunnel.Cells[i].CellType = CellType.Path;
|
||||
@@ -1228,10 +1300,8 @@ namespace Barotrauma
|
||||
private List<VoronoiCell> GetTooCloseCells(List<VoronoiCell> emptyCells, float minDistance)
|
||||
{
|
||||
List<VoronoiCell> tooCloseCells = new List<VoronoiCell>();
|
||||
|
||||
if (minDistance <= 0.0f) { return tooCloseCells; }
|
||||
|
||||
foreach (var cell in emptyCells)
|
||||
foreach (var cell in emptyCells.Distinct())
|
||||
{
|
||||
foreach (var tooCloseCell in GetTooCloseCells(cell.Center, minDistance))
|
||||
{
|
||||
@@ -1241,25 +1311,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*minDistance *= 0.5f;
|
||||
do
|
||||
{
|
||||
tooCloseCells.AddRange(GetTooCloseCells(position, minDistance));
|
||||
|
||||
position += Vector2.Normalize(emptyCells[targetCellIndex].Center - position) * step;
|
||||
|
||||
if (Vector2.Distance(emptyCells[targetCellIndex].Center, position) < step * 2.0f) targetCellIndex++;
|
||||
|
||||
} while (Vector2.Distance(position, emptyCells[emptyCells.Count - 1].Center) > step * 2.0f);*/
|
||||
|
||||
return tooCloseCells;
|
||||
}
|
||||
|
||||
public List<VoronoiCell> GetTooCloseCells(Vector2 position, float minDistance)
|
||||
{
|
||||
HashSet<VoronoiCell> tooCloseCells = new HashSet<VoronoiCell>();
|
||||
var closeCells = GetCells(position, 3);
|
||||
var closeCells = GetCells(position, searchDepth: Math.Max((int)Math.Ceiling(minDistance / GridCellSize), 3));
|
||||
float minDistSqr = minDistance * minDistance;
|
||||
foreach (VoronoiCell cell in closeCells)
|
||||
{
|
||||
@@ -1353,7 +1411,7 @@ namespace Barotrauma
|
||||
int padding = (int)(caveSize.X * 1.2f);
|
||||
Rectangle allowedArea = new Rectangle(padding, padding, Size.X - padding * 2, Size.Y - padding * 2);
|
||||
|
||||
var cavePos = FindPosAwayFromMainPath(radius, asFarAwayAsPossible: true, allowedArea);
|
||||
var cavePos = FindPosAwayFromMainPath((parentTunnel.MinWidth + radius) * 1.2f, asCloseAsPossible: true, allowedArea);
|
||||
|
||||
Point closestParentNode = parentTunnel.Nodes.First();
|
||||
double closestDist = double.PositiveInfinity;
|
||||
@@ -1407,7 +1465,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Tunnel branch in caveBranches)
|
||||
{
|
||||
PositionsOfInterest.Add(new InterestingPosition(branch.Nodes.Last(), PositionType.Cave));
|
||||
PositionsOfInterest.Add(new InterestingPosition(branch.Nodes.Last(), PositionType.Cave, cave));
|
||||
cave.Tunnels.Add(branch);
|
||||
}
|
||||
|
||||
@@ -1426,7 +1484,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateRuin(List<VoronoiCell> mainPath, bool mirror)
|
||||
private void GenerateRuin(Tunnel mainPath, bool mirror)
|
||||
{
|
||||
var ruinGenerationParams = RuinGenerationParams.GetRandom();
|
||||
|
||||
@@ -1435,12 +1493,12 @@ namespace Barotrauma
|
||||
Rand.Range(ruinGenerationParams.SizeMin.Y, ruinGenerationParams.SizeMax.Y, Rand.RandSync.Server));
|
||||
int ruinRadius = Math.Max(ruinSize.X, ruinSize.Y) / 2;
|
||||
|
||||
Point ruinPos = FindPosAwayFromMainPath(ruinRadius + Tunnels.First().MinWidth, asFarAwayAsPossible: false,
|
||||
Point ruinPos = FindPosAwayFromMainPath((ruinRadius + mainPath.MinWidth) * 1.2f, asCloseAsPossible: true,
|
||||
limits: new Rectangle(new Point(ruinSize.X / 2, ruinSize.Y / 2), Size - ruinSize));
|
||||
|
||||
VoronoiCell closestPathCell = null;
|
||||
double closestDist = 0.0f;
|
||||
foreach (VoronoiCell pathCell in mainPath)
|
||||
foreach (VoronoiCell pathCell in mainPath.Cells)
|
||||
{
|
||||
double dist = MathUtils.DistanceSquared(pathCell.Site.Coord.X, pathCell.Site.Coord.Y, ruinPos.X, ruinPos.Y);
|
||||
if (closestPathCell == null || dist < closestDist)
|
||||
@@ -1506,22 +1564,22 @@ namespace Barotrauma
|
||||
CreatePathToClosestTunnel(ruinPos);
|
||||
}
|
||||
|
||||
private Point FindPosAwayFromMainPath(double minDistance, bool asFarAwayAsPossible, Rectangle? limits = null)
|
||||
private Point FindPosAwayFromMainPath(double minDistance, bool asCloseAsPossible, Rectangle? limits = null)
|
||||
{
|
||||
var validPoints = distanceField.FindAll(d => d.Second >= minDistance && (limits == null || limits.Value.Contains(d.First)));
|
||||
validPoints.RemoveAll(d => d.First.Y < GetBottomPosition(d.First.X).Y + minDistance);
|
||||
if (asFarAwayAsPossible || !validPoints.Any())
|
||||
if (asCloseAsPossible || !validPoints.Any())
|
||||
{
|
||||
if (!validPoints.Any()) { validPoints = distanceField; }
|
||||
Pair<Point, double> furthestPoint = null;
|
||||
Pair<Point, double> closestPoint = null;
|
||||
foreach (var point in validPoints)
|
||||
{
|
||||
if (furthestPoint == null || point.Second > furthestPoint.Second)
|
||||
if (closestPoint == null || point.Second < closestPoint.Second)
|
||||
{
|
||||
furthestPoint = point;
|
||||
closestPoint = point;
|
||||
}
|
||||
}
|
||||
return furthestPoint.First;
|
||||
return closestPoint.First;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1581,6 +1639,7 @@ namespace Barotrauma
|
||||
vertices.Add(edge.Point2);
|
||||
}
|
||||
}
|
||||
if (vertices.Count < 3) { return null; }
|
||||
return CreateIceChunk(vertices.Select(v => v - position).ToList(), position, health);
|
||||
}
|
||||
|
||||
@@ -1635,6 +1694,8 @@ namespace Barotrauma
|
||||
|
||||
Vector2 edgeNormal = closestEdge.GetNormal(closestCell);
|
||||
float spireLength = (float)Math.Min(Math.Sqrt(closestDistSqr), maxLength);
|
||||
spireLength *= MathHelper.Lerp(0.3f, 1.5f, Difficulty / 100.0f);
|
||||
|
||||
Vector2 extrudedPoint1 = closestEdge.Point1 + edgeNormal * spireLength * Rand.Range(0.8f, 1.0f, Rand.RandSync.Server);
|
||||
Vector2 extrudedPoint2 = closestEdge.Point2 + edgeNormal * spireLength * Rand.Range(0.8f, 1.0f, Rand.RandSync.Server);
|
||||
List<Vector2> vertices = new List<Vector2>()
|
||||
@@ -2101,7 +2162,7 @@ namespace Barotrauma
|
||||
|
||||
if (resourcesInCluster < 1) { return false; }
|
||||
|
||||
PlaceResources(selectedPrefab, resourcesInCluster, location, out var placedResources, edgeLenght: edgeLength);
|
||||
PlaceResources(selectedPrefab, resourcesInCluster, location, out var placedResources, edgeLength: edgeLength);
|
||||
itemCount += resourcesInCluster;
|
||||
location.InitializeResources();
|
||||
location.Resources.AddRange(placedResources);
|
||||
@@ -2136,7 +2197,7 @@ namespace Barotrauma
|
||||
c.Equals(location) &&
|
||||
c.Resources.Any(r => r != null && !r.Removed &&
|
||||
(!(r.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)))));
|
||||
if(locationHasResources)
|
||||
if (locationHasResources)
|
||||
{
|
||||
allValidLocations.RemoveAt(i);
|
||||
}
|
||||
@@ -2231,10 +2292,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private void PlaceResources(ItemPrefab resourcePrefab, int resourceCount, ClusterLocation location, out List<Item> placedResources,
|
||||
float? edgeLenght = null, float maxResourceOverlap = 0.4f)
|
||||
float? edgeLength = null, float maxResourceOverlap = 0.4f)
|
||||
{
|
||||
edgeLenght ??= Vector2.Distance(location.Edge.Point1, location.Edge.Point2);
|
||||
var minResourceOverlap = -((edgeLenght.Value - (resourceCount * resourcePrefab.Size.X)) / (resourceCount * resourcePrefab.Size.X));
|
||||
edgeLength ??= Vector2.Distance(location.Edge.Point1, location.Edge.Point2);
|
||||
var minResourceOverlap = -((edgeLength.Value - (resourceCount * resourcePrefab.Size.X)) / (resourceCount * resourcePrefab.Size.X));
|
||||
minResourceOverlap = Math.Max(minResourceOverlap, 0.0f);
|
||||
var lerpAmounts = new float[resourceCount];
|
||||
lerpAmounts[0] = 0.0f;
|
||||
@@ -2242,7 +2303,7 @@ namespace Barotrauma
|
||||
for (int i = 1; i < resourceCount; i++)
|
||||
{
|
||||
var overlap = Rand.Range(minResourceOverlap, maxResourceOverlap, sync: Rand.RandSync.Server);
|
||||
lerpAmount += ((1.0f - overlap) * resourcePrefab.Size.X) / edgeLenght.Value;
|
||||
lerpAmount += ((1.0f - overlap) * resourcePrefab.Size.X) / edgeLength.Value;
|
||||
lerpAmounts[i] = Math.Clamp(lerpAmount, 0.0f, 1.0f);
|
||||
}
|
||||
var startOffset = Rand.Range(0.0f, 1.0f - lerpAmount, sync: Rand.RandSync.Server);
|
||||
@@ -2252,7 +2313,9 @@ namespace Barotrauma
|
||||
Vector2 selectedPos = Vector2.Lerp(location.Edge.Point1, location.Edge.Point2, startOffset + lerpAmounts[i]);
|
||||
var item = new Item(resourcePrefab, selectedPos, submarine: null);
|
||||
Vector2 edgeNormal = location.Edge.GetNormal(location.Cell);
|
||||
item.Move(edgeNormal * (item.body == null ? item.Rect.Height / 2 : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent() * 0.7f)), ignoreContacts: true);
|
||||
float moveAmount = (item.body == null ? item.Rect.Height / 2 : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent() * 0.7f));
|
||||
moveAmount += (item.GetComponent<LevelResource>()?.RandomOffsetFromWall ?? 0.0f) * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server);
|
||||
item.Move(edgeNormal * moveAmount, ignoreContacts: true);
|
||||
if (item.GetComponent<Holdable>() is Holdable h)
|
||||
{
|
||||
h.AttachToWall();
|
||||
@@ -2268,7 +2331,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f)
|
||||
public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f, Func<InterestingPosition, bool> filter = null)
|
||||
{
|
||||
if (!PositionsOfInterest.Any())
|
||||
{
|
||||
@@ -2280,7 +2343,7 @@ namespace Barotrauma
|
||||
int tries = 0;
|
||||
do
|
||||
{
|
||||
TryGetInterestingPosition(true, spawnPosType, minDistFromSubs, out Vector2 startPos);
|
||||
TryGetInterestingPosition(true, spawnPosType, minDistFromSubs, out Vector2 startPos, filter);
|
||||
|
||||
Vector2 offset = Rand.Vector(Rand.Range(0.0f, randomSpread, Rand.RandSync.Server), Rand.RandSync.Server);
|
||||
if (!cells.Any(c => c.IsPointInside(startPos + offset)))
|
||||
@@ -2312,14 +2375,14 @@ namespace Barotrauma
|
||||
return position;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position)
|
||||
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);
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, filter);
|
||||
position = pos.ToVector2();
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position)
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Func<InterestingPosition, bool> filter = null)
|
||||
{
|
||||
if (!PositionsOfInterest.Any())
|
||||
{
|
||||
@@ -2328,8 +2391,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<InterestingPosition> suitablePositions = PositionsOfInterest.FindAll(p => positionType.HasFlag(p.PositionType));
|
||||
if (filter != null)
|
||||
{
|
||||
suitablePositions.RemoveAll(p => !filter(p));
|
||||
}
|
||||
//avoid floating ice chunks on the main path
|
||||
if (positionType == PositionType.MainPath || positionType == PositionType.SidePath)
|
||||
if (positionType.HasFlag(PositionType.MainPath) || positionType.HasFlag(PositionType.SidePath))
|
||||
{
|
||||
suitablePositions.RemoveAll(p => ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(p.Position.ToVector2()))));
|
||||
}
|
||||
@@ -2513,26 +2580,38 @@ namespace Barotrauma
|
||||
}
|
||||
cells.Remove(cell);
|
||||
|
||||
//if the edge is very short, remove an adjacent cell to prevent making the passage too narrow
|
||||
if (Vector2.DistanceSquared(e.Point1, e.Point2) < 200.0f * 200.0f)
|
||||
//go through the edges of this cell and find the ones that are next to a removed cell
|
||||
foreach (var otherEdge in cell.Edges)
|
||||
{
|
||||
foreach (GraphEdge e2 in cell.Edges)
|
||||
var otherAdjacent = otherEdge.AdjacentCell(cell);
|
||||
if (otherAdjacent == null || otherAdjacent.CellType == CellType.Solid) { continue; }
|
||||
|
||||
//if the edge is very short, remove adjacent cells to prevent making the passage too narrow
|
||||
if (Vector2.DistanceSquared(otherEdge.Point1, otherEdge.Point2) < 500.0f * 500.0f)
|
||||
{
|
||||
if (e2 == e) { continue; }
|
||||
var adjacentCell = e2.AdjacentCell(cell);
|
||||
if (adjacentCell == null || adjacentCell.CellType == CellType.Removed) { continue; }
|
||||
adjacentCell.CellType = CellType.Removed;
|
||||
for (int x = 0; x < cellGrid.GetLength(0); x++)
|
||||
foreach (GraphEdge e2 in cell.Edges)
|
||||
{
|
||||
for (int y = 0; y < cellGrid.GetLength(1); y++)
|
||||
if (e2 == otherEdge || e2 == otherEdge) { continue; }
|
||||
if (!MathUtils.NearlyEqual(otherEdge.Point1, e2.Point1) && !MathUtils.NearlyEqual(otherEdge.Point2, e2.Point1) && !MathUtils.NearlyEqual(otherEdge.Point2, e2.Point2))
|
||||
{
|
||||
cellGrid[x, y].Remove(adjacentCell);
|
||||
continue;
|
||||
}
|
||||
var adjacentCell = e2.AdjacentCell(cell);
|
||||
if (adjacentCell == null || adjacentCell.CellType == CellType.Removed) { continue; }
|
||||
adjacentCell.CellType = CellType.Removed;
|
||||
for (int x = 0; x < cellGrid.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < cellGrid.GetLength(1); y++)
|
||||
{
|
||||
cellGrid[x, y].Remove(adjacentCell);
|
||||
}
|
||||
}
|
||||
cells.Remove(adjacentCell);
|
||||
}
|
||||
cells.Remove(adjacentCell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -2639,7 +2718,7 @@ namespace Barotrauma
|
||||
{
|
||||
sub.ShowSonarMarker = false;
|
||||
sub.PhysicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
sub.TeamID = Character.TeamType.None;
|
||||
sub.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
tempSW.Stop();
|
||||
Debug.WriteLine($"Sub {sub.Info.Name} loaded in { tempSW.ElapsedMilliseconds} (ms)");
|
||||
@@ -2833,6 +2912,12 @@ namespace Barotrauma
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (Caves.Any(c =>
|
||||
ToolBox.GetWorldBounds(c.Area.Center, c.Area.Size).IntersectsWorld(bounds) ||
|
||||
ToolBox.GetWorldBounds(c.StartPos, new Point(1500)).IntersectsWorld(bounds)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return cells.Any(c => c.Body != null && Vector2.DistanceSquared(pos, c.Center) <= maxDistance && c.BodyVertices.Any(v => bounds.ContainsWorld(v)));
|
||||
}
|
||||
}
|
||||
@@ -3113,15 +3198,26 @@ namespace Barotrauma
|
||||
if (!(GameMain.NetworkMember?.IsClient ?? false))
|
||||
{
|
||||
//empty the reactor
|
||||
foreach (Item item in reactorContainer.Inventory.Items)
|
||||
foreach (Item item in reactorContainer.Inventory.AllItems)
|
||||
{
|
||||
if (item == null) { continue; }
|
||||
if (item.NonInteractable) { continue; }
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
}
|
||||
|
||||
//remove wires
|
||||
foreach (Item item in beaconItems.Where(it => it.GetComponent<Wire>() != null).ToList())
|
||||
{
|
||||
if (item.NonInteractable) { continue; }
|
||||
Wire wire = item.GetComponent<Wire>();
|
||||
if (wire.Locked) { continue; }
|
||||
if (wire.Connections[0] != null && (wire.Connections[0].Item.NonInteractable || wire.Connections[0].Item.GetComponent<ConnectionPanel>().Locked))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (wire.Connections[1] != null && (wire.Connections[1].Item.NonInteractable || wire.Connections[1].Item.GetComponent<ConnectionPanel>().Locked))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.25f)
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
@@ -3131,6 +3227,7 @@ namespace Barotrauma
|
||||
//break powered items
|
||||
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered)))
|
||||
{
|
||||
if (item.NonInteractable) { continue; }
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.5f)
|
||||
{
|
||||
item.Condition *= Rand.Range(0.2f, 0.6f, Rand.RandSync.Unsynced);
|
||||
@@ -3221,7 +3318,7 @@ namespace Barotrauma
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job, randSync: Rand.RandSync.Server);
|
||||
var corpse = Character.Create(CharacterPrefab.HumanConfigFile, worldPos, ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
|
||||
corpse.AnimController.FindHull(worldPos, true);
|
||||
corpse.TeamID = Character.TeamType.None;
|
||||
corpse.TeamID = CharacterTeamType.None;
|
||||
corpse.EnableDespawn = false;
|
||||
selectedPrefab.GiveItems(corpse, wreck);
|
||||
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
|
||||
|
||||
@@ -35,6 +35,11 @@ namespace Barotrauma
|
||||
|
||||
public readonly int InitialDepth;
|
||||
|
||||
/// <summary>
|
||||
/// Determined during level generation based on the size of the submarine. Null if the level hasn't been generated.
|
||||
/// </summary>
|
||||
public int? MinMainPathWidth;
|
||||
|
||||
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
|
||||
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
|
||||
|
||||
|
||||
@@ -52,7 +52,11 @@ namespace Barotrauma
|
||||
|
||||
public Sprite Sprite
|
||||
{
|
||||
get { return spriteIndex < 0 || Prefab.Sprites.Count == 0 ? null : Prefab.Sprites[spriteIndex % Prefab.Sprites.Count]; }
|
||||
get
|
||||
{
|
||||
var prefab = ActivePrefab?.Sprites.Count > 0 ? ActivePrefab : Prefab;
|
||||
return spriteIndex < 0 || prefab.Sprites.Count == 0 ? null : prefab.Sprites[spriteIndex % prefab.Sprites.Count];
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 ISpatialEntity.Position => new Vector2(Position.X, Position.Y);
|
||||
@@ -63,6 +67,8 @@ namespace Barotrauma
|
||||
|
||||
public Submarine Submarine => null;
|
||||
|
||||
public Level.Cave ParentCave;
|
||||
|
||||
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
|
||||
{
|
||||
ActivePrefab = Prefab = prefab;
|
||||
@@ -110,6 +116,19 @@ namespace Barotrauma
|
||||
Triggers.Add(newTrigger);
|
||||
}
|
||||
|
||||
if (spriteIndex == -1)
|
||||
{
|
||||
foreach (var overrideProperties in prefab.OverrideProperties)
|
||||
{
|
||||
if (overrideProperties == null) { continue; }
|
||||
if (overrideProperties.Sprites.Count > 0)
|
||||
{
|
||||
spriteIndex = Rand.Int(overrideProperties.Sprites.Count, Rand.RandSync.Server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NeedsUpdate = NeedsNetworkSyncing || (Triggers != null && Triggers.Any()) || Prefab.PhysicsBodyTriggerIndex > -1;
|
||||
|
||||
InitProjSpecific();
|
||||
|
||||
+55
-9
@@ -171,7 +171,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < cave.CaveGenerationParams.LevelObjectAmount; i++)
|
||||
{
|
||||
//get a random prefab and find a place to spawn it
|
||||
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs);
|
||||
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: true);
|
||||
if (prefab == null) { continue; }
|
||||
if (!suitableSpawnPositions.ContainsKey(prefab))
|
||||
{
|
||||
@@ -184,10 +184,10 @@ namespace Barotrauma
|
||||
}
|
||||
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
|
||||
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
|
||||
PlaceObject(prefab, spawnPosition, level);
|
||||
PlaceObject(prefab, spawnPosition, level, cave);
|
||||
if (prefab.MaxCount < amount)
|
||||
{
|
||||
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
|
||||
if (objects.Count(o => o.Prefab == prefab && o.ParentCave == cave) >= prefab.MaxCount)
|
||||
{
|
||||
availablePrefabs.Remove(prefab);
|
||||
}
|
||||
@@ -196,7 +196,51 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level)
|
||||
public void PlaceNestObjects(Level level, Level.Cave cave, Vector2 nestPosition, float nestRadius, int objectAmount)
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(level.Seed));
|
||||
|
||||
var availablePrefabs = new List<LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.NestWall)));
|
||||
Dictionary<LevelObjectPrefab, List<SpawnPosition>> suitableSpawnPositions = new Dictionary<LevelObjectPrefab, List<SpawnPosition>>();
|
||||
Dictionary<LevelObjectPrefab, List<float>> spawnPositionWeights = new Dictionary<LevelObjectPrefab, List<float>>();
|
||||
|
||||
List<SpawnPosition> availableSpawnPositions = new List<SpawnPosition>();
|
||||
var caveCells = cave.Tunnels.SelectMany(t => t.Cells);
|
||||
List<VoronoiCell> caveWallCells = new List<VoronoiCell>();
|
||||
foreach (var edge in caveCells.SelectMany(c => c.Edges))
|
||||
{
|
||||
if (!edge.NextToCave) { continue; }
|
||||
if (MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), nestPosition.ToPoint()) > nestRadius * nestRadius) { continue; }
|
||||
if (edge.Cell1?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell1); }
|
||||
if (edge.Cell2?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell2); }
|
||||
}
|
||||
availableSpawnPositions.AddRange(GetAvailableSpawnPositions(caveWallCells.Distinct(), LevelObjectPrefab.SpawnPosType.CaveWall));
|
||||
|
||||
for (int i = 0; i < objectAmount; i++)
|
||||
{
|
||||
//get a random prefab and find a place to spawn it
|
||||
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: false);
|
||||
if (prefab == null) { continue; }
|
||||
if (!suitableSpawnPositions.ContainsKey(prefab))
|
||||
{
|
||||
suitableSpawnPositions.Add(prefab,
|
||||
availableSpawnPositions.Where(sp =>
|
||||
sp.Length >= prefab.MinSurfaceWidth &&
|
||||
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
|
||||
spawnPositionWeights.Add(prefab,
|
||||
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
|
||||
}
|
||||
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
|
||||
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
|
||||
PlaceObject(prefab, spawnPosition, level);
|
||||
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
|
||||
{
|
||||
availablePrefabs.Remove(prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level, Level.Cave parentCave = null)
|
||||
{
|
||||
float rotation = 0.0f;
|
||||
if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null)
|
||||
@@ -228,6 +272,7 @@ namespace Barotrauma
|
||||
var newObject = new LevelObject(prefab,
|
||||
new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
|
||||
AddObject(newObject, level);
|
||||
newObject.ParentCave = parentCave;
|
||||
|
||||
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
|
||||
{
|
||||
@@ -237,7 +282,7 @@ namespace Barotrauma
|
||||
var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
|
||||
int prefabCount = matchingPrefabs.Count();
|
||||
var childPrefab = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
|
||||
if (childPrefab == null) continue;
|
||||
if (childPrefab == null) { continue; }
|
||||
|
||||
Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;
|
||||
|
||||
@@ -247,6 +292,7 @@ namespace Barotrauma
|
||||
rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));
|
||||
|
||||
AddObject(childObject, level);
|
||||
childObject.ParentCave = parentCave;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,7 +448,7 @@ namespace Barotrauma
|
||||
return objectsInRange;
|
||||
}
|
||||
|
||||
private List<SpawnPosition> GetAvailableSpawnPositions(IEnumerable<VoronoiCell> cells, LevelObjectPrefab.SpawnPosType spawnPosType)
|
||||
private List<SpawnPosition> GetAvailableSpawnPositions(IEnumerable<VoronoiCell> cells, LevelObjectPrefab.SpawnPosType spawnPosType, bool checkFlags = true)
|
||||
{
|
||||
List<LevelObjectPrefab.SpawnPosType> spawnPosTypes = new List<LevelObjectPrefab.SpawnPosType>(4);
|
||||
List<SpawnPosition> availableSpawnPositions = new List<SpawnPosition>();
|
||||
@@ -498,12 +544,12 @@ namespace Barotrauma
|
||||
availablePrefabs.Select(p => p.GetCommonness(generationParams)).ToList(), Rand.RandSync.Server);
|
||||
}
|
||||
|
||||
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs)
|
||||
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs, bool requireCaveSpecificOverride)
|
||||
{
|
||||
if (availablePrefabs.Sum(p => p.GetCommonness(caveParams)) <= 0.0f) { return null; }
|
||||
if (availablePrefabs.Sum(p => p.GetCommonness(caveParams, requireCaveSpecificOverride)) <= 0.0f) { return null; }
|
||||
return ToolBox.SelectWeightedRandom(
|
||||
availablePrefabs,
|
||||
availablePrefabs.Select(p => p.GetCommonness(caveParams)).ToList(), Rand.RandSync.Server);
|
||||
availablePrefabs.Select(p => p.GetCommonness(caveParams, requireCaveSpecificOverride)).ToList(), Rand.RandSync.Server);
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
|
||||
+8
-7
@@ -37,11 +37,12 @@ namespace Barotrauma
|
||||
MainPathWall = 1,
|
||||
SidePathWall = 2,
|
||||
CaveWall = 4,
|
||||
RuinWall = 8,
|
||||
SeaFloor = 16,
|
||||
MainPath = 32,
|
||||
LevelStart = 64,
|
||||
LevelEnd = 128,
|
||||
NestWall = 8,
|
||||
RuinWall = 16,
|
||||
SeaFloor = 32,
|
||||
MainPath = 64,
|
||||
LevelStart = 128,
|
||||
LevelEnd = 256,
|
||||
Wall = MainPathWall | SidePathWall | CaveWall,
|
||||
}
|
||||
|
||||
@@ -442,14 +443,14 @@ namespace Barotrauma
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
|
||||
public float GetCommonness(CaveGenerationParams generationParams)
|
||||
public float GetCommonness(CaveGenerationParams generationParams, bool requireCaveSpecificOverride = true)
|
||||
{
|
||||
if (generationParams?.Identifier != null &&
|
||||
OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness))
|
||||
{
|
||||
return commonness;
|
||||
}
|
||||
return 0.0f;
|
||||
return requireCaveSpecificOverride ? 0.0f : Commonness;
|
||||
}
|
||||
|
||||
public float GetCommonness(LevelGenerationParams generationParams)
|
||||
|
||||
@@ -34,44 +34,38 @@ namespace Barotrauma
|
||||
|
||||
public Action<LevelTrigger, Entity> OnTriggered;
|
||||
|
||||
private PhysicsBody physicsBody;
|
||||
|
||||
/// <summary>
|
||||
/// Effects applied to entities that are inside the trigger
|
||||
/// </summary>
|
||||
private List<StatusEffect> statusEffects = new List<StatusEffect>();
|
||||
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>();
|
||||
|
||||
/// <summary>
|
||||
/// Attacks applied to entities that are inside the trigger
|
||||
/// </summary>
|
||||
private List<Attack> attacks = new List<Attack>();
|
||||
private readonly List<Attack> attacks = new List<Attack>();
|
||||
|
||||
private float cameraShake;
|
||||
private readonly float cameraShake;
|
||||
private Vector2 unrotatedForce;
|
||||
private float forceFluctuationTimer, currentForceFluctuation = 1.0f;
|
||||
|
||||
private HashSet<Entity> triggerers = new HashSet<Entity>();
|
||||
private readonly HashSet<Entity> triggerers = new HashSet<Entity>();
|
||||
|
||||
private TriggererType triggeredBy;
|
||||
private readonly TriggererType triggeredBy;
|
||||
|
||||
private float randomTriggerInterval;
|
||||
private float randomTriggerProbability;
|
||||
private readonly float randomTriggerInterval;
|
||||
private readonly float randomTriggerProbability;
|
||||
private float randomTriggerTimer;
|
||||
|
||||
private float triggeredTimer;
|
||||
|
||||
//how far away this trigger can activate other triggers from
|
||||
private float triggerOthersDistance;
|
||||
|
||||
private HashSet<string> tags = new HashSet<string>();
|
||||
private readonly HashSet<string> tags = new HashSet<string>();
|
||||
|
||||
//other triggers have to have at least one of these tags to trigger this one
|
||||
private HashSet<string> allowedOtherTriggerTags = new HashSet<string>();
|
||||
private readonly HashSet<string> allowedOtherTriggerTags = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// How long the trigger stays in the triggered state after triggerers have left
|
||||
/// </summary>
|
||||
private float stayTriggeredDelay;
|
||||
private readonly float stayTriggeredDelay;
|
||||
|
||||
public LevelTrigger ParentTrigger;
|
||||
|
||||
@@ -88,30 +82,24 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
worldPosition = value;
|
||||
physicsBody?.SetTransform(ConvertUnits.ToSimUnits(value), physicsBody.Rotation);
|
||||
PhysicsBody?.SetTransform(ConvertUnits.ToSimUnits(value), PhysicsBody.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get { return physicsBody == null ? 0.0f : physicsBody.Rotation; }
|
||||
get { return PhysicsBody == null ? 0.0f : PhysicsBody.Rotation; }
|
||||
set
|
||||
{
|
||||
if (physicsBody == null) return;
|
||||
physicsBody.SetTransform(physicsBody.Position, value);
|
||||
if (PhysicsBody == null) return;
|
||||
PhysicsBody.SetTransform(PhysicsBody.Position, value);
|
||||
CalculateDirectionalForce();
|
||||
}
|
||||
}
|
||||
|
||||
public PhysicsBody PhysicsBody
|
||||
{
|
||||
get { return physicsBody; }
|
||||
}
|
||||
public PhysicsBody PhysicsBody { get; private set; }
|
||||
|
||||
public float TriggerOthersDistance
|
||||
{
|
||||
get { return triggerOthersDistance; }
|
||||
}
|
||||
public float TriggerOthersDistance { get; private set; }
|
||||
|
||||
public IEnumerable<Entity> Triggerers
|
||||
{
|
||||
@@ -153,7 +141,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
private TriggerForceMode forceMode;
|
||||
private readonly TriggerForceMode forceMode;
|
||||
public TriggerForceMode ForceMode
|
||||
{
|
||||
get { return forceMode; }
|
||||
@@ -198,6 +186,9 @@ namespace Barotrauma
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool triggeredOnce;
|
||||
private readonly bool triggerOnce;
|
||||
|
||||
public LevelTrigger(XElement element, Vector2 position, float rotation, float scale = 1.0f, string parentDebugName = "")
|
||||
{
|
||||
@@ -206,20 +197,20 @@ namespace Barotrauma
|
||||
worldPosition = position;
|
||||
if (element.Attributes("radius").Any() || element.Attributes("width").Any() || element.Attributes("height").Any())
|
||||
{
|
||||
physicsBody = new PhysicsBody(element, scale)
|
||||
PhysicsBody = new PhysicsBody(element, scale)
|
||||
{
|
||||
CollisionCategories = Physics.CollisionLevel,
|
||||
CollidesWith = Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionProjectile | Physics.CollisionWall
|
||||
};
|
||||
physicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
|
||||
physicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
|
||||
physicsBody.FarseerBody.SetIsSensor(true);
|
||||
physicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
physicsBody.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
|
||||
PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
|
||||
PhysicsBody.FarseerBody.SetIsSensor(true);
|
||||
PhysicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
PhysicsBody.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
|
||||
ColliderRadius = ConvertUnits.ToDisplayUnits(Math.Max(Math.Max(PhysicsBody.radius, PhysicsBody.width / 2.0f), PhysicsBody.height / 2.0f));
|
||||
|
||||
physicsBody.SetTransform(ConvertUnits.ToSimUnits(position), rotation);
|
||||
PhysicsBody.SetTransform(ConvertUnits.ToSimUnits(position), rotation);
|
||||
}
|
||||
|
||||
cameraShake = element.GetAttributeFloat("camerashake", 0.0f);
|
||||
@@ -227,6 +218,8 @@ namespace Barotrauma
|
||||
InfectIdentifier = element.GetAttributeString("infectidentifier", null);
|
||||
InfectionChance = element.GetAttributeFloat("infectionchance", 0.05f);
|
||||
|
||||
triggerOnce = element.GetAttributeBool("triggeronce", false);
|
||||
|
||||
stayTriggeredDelay = element.GetAttributeFloat("staytriggereddelay", 0.0f);
|
||||
randomTriggerInterval = element.GetAttributeFloat("randomtriggerinterval", 0.0f);
|
||||
randomTriggerProbability = element.GetAttributeFloat("randomtriggerprobability", 0.0f);
|
||||
@@ -256,7 +249,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Error in LevelTrigger config: \"" + triggeredByStr + "\" is not a valid triggerer type.");
|
||||
}
|
||||
UpdateCollisionCategories();
|
||||
triggerOthersDistance = element.GetAttributeFloat("triggerothersdistance", 0.0f);
|
||||
TriggerOthersDistance = element.GetAttributeFloat("triggerothersdistance", 0.0f);
|
||||
|
||||
var tagsArray = element.GetAttributeStringArray("tags", new string[0]);
|
||||
foreach (string tag in tagsArray)
|
||||
@@ -283,11 +276,14 @@ namespace Barotrauma
|
||||
case "attack":
|
||||
case "damage":
|
||||
var attack = new Attack(subElement, string.IsNullOrEmpty(parentDebugName) ? "LevelTrigger" : "LevelTrigger in " + parentDebugName);
|
||||
var multipliedAfflictions = attack.GetMultipliedAfflictions((float)Timing.Step);
|
||||
attack.Afflictions.Clear();
|
||||
foreach (Affliction affliction in multipliedAfflictions)
|
||||
if (!triggerOnce)
|
||||
{
|
||||
attack.Afflictions.Add(affliction, null);
|
||||
var multipliedAfflictions = attack.GetMultipliedAfflictions((float)Timing.Step);
|
||||
attack.Afflictions.Clear();
|
||||
foreach (Affliction affliction in multipliedAfflictions)
|
||||
{
|
||||
attack.Afflictions.Add(affliction, null);
|
||||
}
|
||||
}
|
||||
attacks.Add(attack);
|
||||
break;
|
||||
@@ -300,14 +296,14 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateCollisionCategories()
|
||||
{
|
||||
if (physicsBody == null) return;
|
||||
if (PhysicsBody == null) return;
|
||||
|
||||
var collidesWith = Physics.CollisionNone;
|
||||
if (triggeredBy.HasFlag(TriggererType.Character) || triggeredBy.HasFlag(TriggererType.Creature)) collidesWith |= Physics.CollisionCharacter;
|
||||
if (triggeredBy.HasFlag(TriggererType.Item)) collidesWith |= Physics.CollisionItem | Physics.CollisionProjectile;
|
||||
if (triggeredBy.HasFlag(TriggererType.Submarine)) collidesWith |= Physics.CollisionWall;
|
||||
if (triggeredBy.HasFlag(TriggererType.Human) || triggeredBy.HasFlag(TriggererType.Creature)) { collidesWith |= Physics.CollisionCharacter; }
|
||||
if (triggeredBy.HasFlag(TriggererType.Item)) { collidesWith |= Physics.CollisionItem | Physics.CollisionProjectile; }
|
||||
if (triggeredBy.HasFlag(TriggererType.Submarine)) { collidesWith |= Physics.CollisionWall; }
|
||||
|
||||
physicsBody.CollidesWith = collidesWith;
|
||||
PhysicsBody.CollidesWith = collidesWith;
|
||||
}
|
||||
|
||||
private void CalculateDirectionalForce()
|
||||
@@ -362,7 +358,7 @@ namespace Barotrauma
|
||||
private void PhysicsBody_OnSeparation(Fixture fixtureA, Fixture fixtureB, Contact contact)
|
||||
{
|
||||
Entity entity = GetEntity(fixtureB);
|
||||
if (entity == null) return;
|
||||
if (entity == null) { return; }
|
||||
|
||||
if (entity is Character character &&
|
||||
(!character.Enabled || character.Removed) &&
|
||||
@@ -376,22 +372,25 @@ namespace Barotrauma
|
||||
//check if there are contacts with any other fixture of the trigger
|
||||
//(the OnSeparation callback happens when two fixtures separate,
|
||||
//e.g. if a body stops touching the circular fixture at the end of a capsule-shaped body)
|
||||
ContactEdge contactEdge = fixtureA.Body.ContactList;
|
||||
while (contactEdge != null)
|
||||
foreach (Fixture fixture in PhysicsBody.FarseerBody.FixtureList)
|
||||
{
|
||||
if (contactEdge.Contact != null &&
|
||||
contactEdge.Contact.Enabled &&
|
||||
contactEdge.Contact.IsTouching)
|
||||
ContactEdge contactEdge = fixture.Body.ContactList;
|
||||
while (contactEdge != null)
|
||||
{
|
||||
if (contactEdge.Contact.FixtureA != fixtureA && contactEdge.Contact.FixtureB != fixtureA)
|
||||
if (contactEdge.Contact != null &&
|
||||
contactEdge.Contact.Enabled &&
|
||||
contactEdge.Contact.IsTouching)
|
||||
{
|
||||
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
|
||||
contactEdge.Contact.FixtureB :
|
||||
contactEdge.Contact.FixtureA);
|
||||
if (otherEntity == entity) { return; }
|
||||
if (contactEdge.Contact.FixtureA != fixture && contactEdge.Contact.FixtureB != fixture)
|
||||
{
|
||||
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
|
||||
contactEdge.Contact.FixtureB :
|
||||
contactEdge.Contact.FixtureA);
|
||||
if (otherEntity == entity) { return; }
|
||||
}
|
||||
}
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
|
||||
if (triggerers.Contains(entity))
|
||||
@@ -403,10 +402,10 @@ namespace Barotrauma
|
||||
|
||||
private Entity GetEntity(Fixture fixture)
|
||||
{
|
||||
if (fixture.Body == null || fixture.Body.UserData == null) return null;
|
||||
if (fixture.Body.UserData is Entity entity) return entity;
|
||||
if (fixture.Body.UserData is Limb limb) return limb.character;
|
||||
if (fixture.Body.UserData is SubmarineBody subBody) return subBody.Submarine;
|
||||
if (fixture.Body == null || fixture.Body.UserData == null) { return null; }
|
||||
if (fixture.Body.UserData is Entity entity) { return entity; }
|
||||
if (fixture.Body.UserData is Limb limb) { return limb.character; }
|
||||
if (fixture.Body.UserData is SubmarineBody subBody) { return subBody.Submarine; }
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -416,15 +415,15 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void OtherTriggered(LevelObject levelObject, LevelTrigger otherTrigger)
|
||||
{
|
||||
if (!triggeredBy.HasFlag(TriggererType.OtherTrigger) || stayTriggeredDelay <= 0.0f) return;
|
||||
if (!triggeredBy.HasFlag(TriggererType.OtherTrigger) || stayTriggeredDelay <= 0.0f) { return; }
|
||||
|
||||
//check if the other trigger has appropriate tags
|
||||
if (allowedOtherTriggerTags.Count > 0)
|
||||
{
|
||||
if (!allowedOtherTriggerTags.Any(t => otherTrigger.tags.Contains(t))) return;
|
||||
if (!allowedOtherTriggerTags.Any(t => otherTrigger.tags.Contains(t))) { return; }
|
||||
}
|
||||
|
||||
if (Vector2.DistanceSquared(WorldPosition, otherTrigger.WorldPosition) <= otherTrigger.triggerOthersDistance * otherTrigger.triggerOthersDistance)
|
||||
if (Vector2.DistanceSquared(WorldPosition, otherTrigger.WorldPosition) <= otherTrigger.TriggerOthersDistance * otherTrigger.TriggerOthersDistance)
|
||||
{
|
||||
bool wasAlreadyTriggered = IsTriggered;
|
||||
triggeredTimer = stayTriggeredDelay;
|
||||
@@ -441,10 +440,10 @@ namespace Barotrauma
|
||||
|
||||
triggerers.RemoveWhere(t => t.Removed);
|
||||
|
||||
if (physicsBody != null)
|
||||
if (PhysicsBody != null)
|
||||
{
|
||||
//failsafe to ensure triggerers get removed when they're far from the trigger
|
||||
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(physicsBody.GetMaxExtent() * 5), 5000.0f);
|
||||
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(PhysicsBody.GetMaxExtent() * 5), 5000.0f);
|
||||
triggerers.RemoveWhere(t =>
|
||||
{
|
||||
return Vector2.Distance(t.WorldPosition, WorldPosition) > maxExtent;
|
||||
@@ -500,17 +499,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerOnce)
|
||||
{
|
||||
if (triggeredOnce) { return; }
|
||||
if (triggerers.Count > 0) { triggeredOnce = true; }
|
||||
}
|
||||
|
||||
foreach (Entity triggerer in triggerers)
|
||||
{
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (triggerer is Character)
|
||||
Vector2? position = null;
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This)) { position = WorldPosition; }
|
||||
if (triggerer is Character character)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, triggerer, (Character)triggerer);
|
||||
effect.Apply(effect.type, deltaTime, triggerer, character, position);
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Contained) && character.Inventory != null)
|
||||
{
|
||||
foreach (Item item in character.Inventory.AllItemsMod)
|
||||
{
|
||||
if (item.ContainedItems == null) { continue; }
|
||||
foreach (Item containedItem in item.ContainedItems)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, triggerer, containedItem.AllPropertyObjects, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (triggerer is Item)
|
||||
else if (triggerer is Item item)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, triggerer, ((Item)triggerer).AllPropertyObjects);
|
||||
effect.Apply(effect.type, deltaTime, triggerer, item.AllPropertyObjects, position);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
var targets = new List<ISerializableEntity>();
|
||||
effect.GetNearbyTargets(worldPosition, targets);
|
||||
effect.Apply(effect.type, deltaTime, triggerer, targets);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
#if DEBUG
|
||||
using System.Xml;
|
||||
#else
|
||||
using Barotrauma.IO;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Barotrauma.RuinGeneration
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user