Files
LuaCsForBarotraumaEP/Subsurface/Source/Map/Levels/Level.cs

949 lines
33 KiB
C#

using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using Voronoi2;
using Barotrauma.RuinGeneration;
namespace Barotrauma
{
class Level
{
public const float ShaftHeight = 1000.0f;
public static Level Loaded
{
get { return loaded; }
}
[Flags]
public enum PositionType
{
MainPath=1, Cave=2, Ruin=4
}
struct InterestingPosition
{
public readonly Vector2 Position;
public readonly PositionType PositionType;
public InterestingPosition(Vector2 position, PositionType positionType)
{
Position = position;
PositionType = positionType;
}
}
static Level loaded;
private LevelRenderer renderer;
//how close the sub has to be to start/endposition to exit
public const float ExitDistance = 6000.0f;
private string seed;
public const int GridCellSize = 2000;
private List<VoronoiCell>[,] cellGrid;
private WrappingWall[,] wrappingWalls;
//private float shaftHeight;
//List<Body> bodies;
private List<VoronoiCell> cells;
//private VertexBuffer vertexBuffer;
private Vector2 startPosition, endPosition;
private Rectangle borders;
private List<Body> bodies;
private List<InterestingPosition> positionsOfInterest;
private List<Ruin> ruins;
private Color backgroundColor;
private LevelGenerationParams generationParams;
public Vector2 StartPosition
{
get { return startPosition; }
}
public Vector2 Size
{
get { return new Vector2(borders.Width, borders.Height); }
}
public Vector2 EndPosition
{
get { return endPosition; }
}
public List<Ruin> Ruins
{
get { return ruins; }
}
public WrappingWall[,] WrappingWalls
{
get { return wrappingWalls; }
}
public string Seed
{
get { return seed; }
}
public float Difficulty
{
get;
private set;
}
public Body ShaftBody
{
get;
private set;
}
public LevelGenerationParams GenerationParams
{
get { return generationParams; }
}
public Color BackgroundColor
{
get { return backgroundColor; }
}
public Level(string seed, float difficulty, LevelGenerationParams generationParams)
{
this.seed = seed;
this.Difficulty = difficulty;
this.generationParams = generationParams;
borders = new Rectangle(0, 0, (int)generationParams.Width, (int)generationParams.Height);
}
public static Level CreateRandom(LocationConnection locationConnection)
{
string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name;
return new Level(seed, locationConnection.Difficulty, LevelGenerationParams.GetRandom(seed));
}
public static Level CreateRandom(string seed = "")
{
if (seed == "")
{
seed = Rand.Range(0, int.MaxValue, false).ToString();
}
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
return new Level(seed, Rand.Range(30.0f, 80.0f, false), LevelGenerationParams.GetRandom(seed));
}
public void Generate(bool mirror = false)
{
Stopwatch sw = new Stopwatch();
sw.Start();
if (loaded != null) loaded.Unload();
loaded = this;
positionsOfInterest = new List<InterestingPosition>();
renderer = new LevelRenderer(this);
Voronoi voronoi = new Voronoi(1.0);
List<Vector2> sites = new List<Vector2>();
bodies = new List<Body>();
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
backgroundColor = generationParams.BackgroundColor;
float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3;
GameMain.LightManager.AmbientLight = new Color(backgroundColor * (10.0f / avgValue), 1.0f);
float minWidth = Submarine.MainSub == null ? 0.0f : Math.Max(Submarine.MainSub.Borders.Width, Submarine.MainSub.Borders.Height);
minWidth = Math.Max(minWidth, 6500.0f);
startPosition = new Vector2(
Rand.Range(minWidth * 2, minWidth * 4, false),
Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false));
endPosition = new Vector2(
borders.Width - Rand.Range(minWidth * 2, minWidth * 4, false),
Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false));
List<Vector2> pathNodes = new List<Vector2>();
Rectangle pathBorders = borders;// new Rectangle((int)minWidth, (int)minWidth, borders.Width - (int)minWidth * 2, borders.Height - (int)minWidth);
pathBorders.Inflate(-minWidth*2, -minWidth*2);
pathNodes.Add(new Vector2(startPosition.X, borders.Height));
Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange;
for (float x = startPosition.X + Rand.Range(nodeInterval.X, nodeInterval.Y, false);
x < endPosition.X - Rand.Range(nodeInterval.X, nodeInterval.Y, false);
x += Rand.Range(nodeInterval.X, nodeInterval.Y, false))
{
pathNodes.Add(new Vector2(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, false)));
}
pathNodes.Add(new Vector2(endPosition.X, borders.Height));
List<List<Vector2>> smallTunnels = new List<List<Vector2>>();
for (int i = 0; i < generationParams.SmallTunnelCount; i++)
{
var tunnelStartPos = pathNodes[Rand.Range(2, pathNodes.Count - 2, false)];
tunnelStartPos.X = MathHelper.Clamp(tunnelStartPos.X, pathBorders.X, pathBorders.Right);
float tunnelLength = Rand.Range(
generationParams.SmallTunnelLengthRange.X,
generationParams.SmallTunnelLengthRange.Y,
false);
var tunnelNodes = MathUtils.GenerateJaggedLine(
tunnelStartPos,
new Vector2(tunnelStartPos.X, pathBorders.Bottom)+Rand.Vector(tunnelLength,false),
4, 1000.0f);
List<Vector2> tunnel = new List<Vector2>();
foreach (Vector2[] tunnelNode in tunnelNodes)
{
if (!pathBorders.Contains(tunnelNode[0])) continue;
tunnel.Add(tunnelNode[0]);
}
if (tunnel.Any()) smallTunnels.Add(tunnel);
}
Vector2 siteInterval = generationParams.VoronoiSiteInterval;
Vector2 siteVariance = generationParams.VoronoiSiteVariance;
for (float x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X)
{
for (float y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y)
{
Vector2 site = new Vector2(
x + Rand.Range(-siteVariance.X, siteVariance.X, false),
y + Rand.Range(-siteVariance.Y, siteVariance.Y, false));
if (smallTunnels.Any(t => t.Any(node => Vector2.Distance(node, site) < siteInterval.Length())))
{
//add some more sites around the small tunnels to generate more small voronoi cells
if (x < borders.Width - siteInterval.X) sites.Add(new Vector2(x, y) + Vector2.UnitX * siteInterval * 0.5f);
if (y < borders.Height - siteInterval.Y) sites.Add(new Vector2(x, y) + Vector2.UnitY * siteInterval * 0.5f);
if (x < borders.Width - siteInterval.X && y < borders.Height - siteInterval.Y) sites.Add(new Vector2(x, y) + Vector2.One * siteInterval * 0.5f);
}
if (mirror) site.X = borders.Width - site.X;
sites.Add(site);
}
}
Stopwatch sw2 = new Stopwatch();
sw2.Start();
List<GraphEdge> graphEdges = voronoi.MakeVoronoiGraph(sites, borders.Width, borders.Height);
Debug.WriteLine("MakeVoronoiGraph: " + sw2.ElapsedMilliseconds + " ms");
sw2.Restart();
//construct voronoi cells based on the graph edges
cells = CaveGenerator.GraphEdgesToCells(graphEdges, borders, GridCellSize, out cellGrid);
Debug.WriteLine("find cells: " + sw2.ElapsedMilliseconds + " ms");
sw2.Restart();
List<VoronoiCell> mainPath = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize,
new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.5f, mirror);
for (int i = 2; i < mainPath.Count; i += 3)
{
positionsOfInterest.Add(new InterestingPosition(mainPath[i].Center, PositionType.MainPath));
}
List<VoronoiCell> pathCells = new List<VoronoiCell>(mainPath);
EnlargeMainPath(pathCells, minWidth);
foreach (InterestingPosition positionOfInterest in positionsOfInterest)
{
WayPoint wayPoint = new WayPoint(positionOfInterest.Position, SpawnType.Enemy, null);
wayPoint.MoveWithLevel = true;
}
startPosition.X = pathCells[0].Center.X;
foreach (List<Vector2> tunnel in smallTunnels)
{
if (tunnel.Count<2) continue;
//find the cell which the path starts from
int startCellIndex = CaveGenerator.FindCellIndex(tunnel[0], cells, cellGrid, GridCellSize, 1);
if (startCellIndex < 0) continue;
//if it wasn't one of the cells in the main path, don't create a tunnel
if (cells[startCellIndex].CellType != CellType.Path) continue;
var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders);
positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), PositionType.Cave));
if (tunnel.Count > 4) positionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count / 2], PositionType.Cave));
pathCells.AddRange(newPathCells);
}
Debug.WriteLine("path: " + sw2.ElapsedMilliseconds + " ms");
sw2.Restart();
cells = CleanCells(pathCells);
pathCells.AddRange(CreateBottomHoles(generationParams.BottomHoleProbability, new Rectangle(
(int)(borders.Width * 0.2f), 0,
(int)(borders.Width * 0.6f), (int)(borders.Height * 0.8f))));
foreach (VoronoiCell cell in cells)
{
if (cell.Center.Y < borders.Height / 2) continue;
cell.edges.ForEach(e => e.OutsideLevel = true);
}
foreach (VoronoiCell cell in pathCells)
{
cell.edges.ForEach(e => e.OutsideLevel = false);
cell.CellType = CellType.Path;
cells.Remove(cell);
}
//generate some narrow caves
int caveAmount = 0;// Rand.Int(3, false);
List<VoronoiCell> usedCaveCells = new List<VoronoiCell>();
for (int i = 0; i < caveAmount; i++)
{
Vector2 startPoint = Vector2.Zero;
VoronoiCell startCell = null;
var caveCells = new List<VoronoiCell>();
int maxTries = 5, tries = 0;
while (tries<maxTries)
{
startCell = cells[Rand.Int(cells.Count, false)];
//find an edge between the cell and the already carved path
GraphEdge startEdge =
startCell.edges.Find(e => pathCells.Contains(e.AdjacentCell(startCell)));
if (startEdge != null)
{
startPoint = (startEdge.point1 + startEdge.point2) / 2.0f;
startPoint += startPoint - startCell.Center;
//get the cells in which the cave will be carved
caveCells = GetCells(startCell.Center, 2);
//remove cells that have already been "carved" out
caveCells.RemoveAll(c => c.CellType == CellType.Path);
//if any of the cells have already been used as a cave, continue and find some other cells
if (usedCaveCells.Any(c => caveCells.Contains(c))) continue;
break;
}
tries++;
}
//couldn't find a place for a cave -> abort
if (tries >= maxTries) break;
if (!caveCells.Any()) continue;
usedCaveCells.AddRange(caveCells);
List<VoronoiCell> caveSolidCells;
var cavePathCells = CaveGenerator.CarveCave(caveCells, startPoint, out caveSolidCells);
//remove the large cells used as a "base" for the cave (they've now been replaced with smaller ones)
caveCells.ForEach(c => cells.Remove(c));
cells.AddRange(caveSolidCells);
foreach (VoronoiCell cell in cavePathCells)
{
cells.Remove(cell);
}
pathCells.AddRange(cavePathCells);
for (int j = cavePathCells.Count / 2; j < cavePathCells.Count; j += 10)
{
positionsOfInterest.Add(new InterestingPosition(cavePathCells[j].Center, PositionType.Cave));
}
}
for (int x = 0; x < cellGrid.GetLength(0); x++)
{
for (int y = 0; y < cellGrid.GetLength(1); y++)
{
cellGrid[x, y].Clear();
}
}
foreach (VoronoiCell cell in cells)
{
int x = (int)Math.Floor(cell.Center.X / GridCellSize);
int y = (int)Math.Floor(cell.Center.Y / GridCellSize);
if (x < 0 || y < 0 || x >= cellGrid.GetLength(0) || y >= cellGrid.GetLength(1)) continue;
cellGrid[x, y].Add(cell);
}
for (int i = 0; i<generationParams.RuinCount; i++)
{
GenerateRuin(mainPath);
}
startPosition.Y = borders.Height;
endPosition.Y = borders.Height;
List<VoronoiCell> cellsWithBody = new List<VoronoiCell>(cells);
List<VertexPositionColor> bodyVertices;
bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out bodyVertices);
renderer.SetBodyVertices(bodyVertices.ToArray());
renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells));
renderer.PlaceSprites(generationParams.BackgroundSpriteAmount);
wrappingWalls = new WrappingWall[2, 2];
Rectangle ignoredArea = new Rectangle((int)startPosition.X, 0, (int)(endPosition.X - startPosition.X), borders.Height);
for (int side = 0; side < 2; side++)
{
for (int i = 0; i < 2; i++)
{
wrappingWalls[side, i] = new WrappingWall(pathCells, cells, ignoredArea,
(side == 0 ? -1 : 1) * (i + 1));
List<VertexPositionColor> wrappingWallVertices;
CaveGenerator.GeneratePolygons(wrappingWalls[side, i].Cells, out wrappingWallVertices, false);
wrappingWalls[side, i].SetBodyVertices(wrappingWallVertices.ToArray());
wrappingWalls[side, i].SetWallVertices(CaveGenerator.GenerateWallShapes(wrappingWalls[side, i].Cells));
}
}
for (int side = 0; side < 2; side++)
{
for (int i = 0; i < 2; i++)
{
cells.AddRange(wrappingWalls[side, i].Cells);
}
}
ShaftBody = BodyFactory.CreateEdge(GameMain.World,
ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)),
ConvertUnits.ToSimUnits(new Vector2(borders.Right, 0)));
ShaftBody.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, borders.Height)), 0.0f);
ShaftBody.BodyType = BodyType.Static;
ShaftBody.CollisionCategories = Physics.CollisionLevel;
bodies.Add(ShaftBody);
foreach (VoronoiCell cell in cells)
{
foreach (GraphEdge edge in cell.edges)
{
edge.cell1 = null;
edge.cell2 = null;
edge.site1 = null;
edge.site2 = null;
}
}
Debug.WriteLine("Generatelevel: " + sw2.ElapsedMilliseconds + " ms");
sw2.Restart();
if (mirror)
{
Vector2 temp = startPosition;
startPosition = endPosition;
endPosition = temp;
}
Debug.WriteLine("**********************************************************************************");
Debug.WriteLine("Generated a map with " + sites.Count + " sites in " + sw.ElapsedMilliseconds + " ms");
Debug.WriteLine("Seed: "+seed);
Debug.WriteLine("**********************************************************************************");
}
private List<VoronoiCell> CreateBottomHoles(float holeProbability, Rectangle limits)
{
List<VoronoiCell> toBeRemoved = new List<VoronoiCell>();
foreach (VoronoiCell cell in cells)
{
if (Rand.Range(0.0f, 1.0f, false) > holeProbability) continue;
if (!limits.Contains(cell.Center)) continue;
float closestDist = 0.0f;
WayPoint closestWayPoint = null;
foreach (WayPoint wp in WayPoint.WayPointList)
{
if (wp.SpawnType != SpawnType.Path) continue;
float dist =Math.Abs(cell.Center.X - wp.WorldPosition.X);
if (closestWayPoint == null || dist < closestDist)
{
closestDist = dist;
closestWayPoint = wp;
}
}
if (closestWayPoint.WorldPosition.Y < cell.Center.Y) continue;
toBeRemoved.Add(cell);
}
return toBeRemoved;
}
private void EnlargeMainPath(List<VoronoiCell> pathCells, float minWidth)
{
List<WayPoint> wayPoints = new List<WayPoint>();
var newWaypoint = new WayPoint(new Rectangle((int)pathCells[0].Center.X, borders.Height, 10, 10), null);
newWaypoint.MoveWithLevel = true;
wayPoints.Add(newWaypoint);
//WayPoint prevWaypoint = newWaypoint;
for (int i = 0; i < pathCells.Count; i++)
{
////clean "loops" from the path
//for (int n = 0; n < i; n++)
//{
// if (pathCells[n] != pathCells[i]) continue;
// pathCells.RemoveRange(n + 1, i - n);
// break;
//}
//if (i >= pathCells.Count) break;
pathCells[i].CellType = CellType.Path;
newWaypoint = new WayPoint(new Rectangle((int)pathCells[i].Center.X, (int)pathCells[i].Center.Y, 10, 10), null);
newWaypoint.MoveWithLevel = true;
wayPoints.Add(newWaypoint);
wayPoints[wayPoints.Count-2].linkedTo.Add(newWaypoint);
newWaypoint.linkedTo.Add(wayPoints[wayPoints.Count - 2]);
for (int n = 0; n < wayPoints.Count; n++)
{
if (wayPoints[n].Position != newWaypoint.Position) continue;
wayPoints[n].linkedTo.Add(newWaypoint);
newWaypoint.linkedTo.Add(wayPoints[n]);
break;
}
//prevWaypoint = newWaypoint;
}
newWaypoint = new WayPoint(new Rectangle((int)pathCells[pathCells.Count - 1].Center.X, borders.Height, 10, 10), null);
newWaypoint.MoveWithLevel = true;
wayPoints.Add(newWaypoint);
wayPoints[wayPoints.Count - 2].linkedTo.Add(newWaypoint);
newWaypoint.linkedTo.Add(wayPoints[wayPoints.Count - 2]);
if (minWidth > 0.0f)
{
List<VoronoiCell> removedCells = GetTooCloseCells(pathCells, minWidth);
foreach (VoronoiCell removedCell in removedCells)
{
if (removedCell.CellType == CellType.Path) continue;
pathCells.Add(removedCell);
removedCell.CellType = CellType.Path;
}
}
}
private List<VoronoiCell> GetTooCloseCells(List<VoronoiCell> emptyCells, float minDistance)
{
List<VoronoiCell> tooCloseCells = new List<VoronoiCell>();
Vector2 position = emptyCells[0].Center;
if (minDistance == 0.0f) return tooCloseCells;
float step = 100.0f;
int targetCellIndex = 1;
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;
}
private List<VoronoiCell> GetTooCloseCells(Vector2 position, float minDistance)
{
List<VoronoiCell> tooCloseCells = new List<VoronoiCell>();
var closeCells = GetCells(position, 3);
foreach (VoronoiCell cell in closeCells)
{
bool tooClose = false;
foreach (GraphEdge edge in cell.edges)
{
if (Vector2.Distance(edge.point1, position) < minDistance ||
Vector2.Distance(edge.point2, position) < minDistance)
{
tooClose = true;
break;
}
}
if (tooClose && !tooCloseCells.Contains(cell)) tooCloseCells.Add(cell);
}
return tooCloseCells;
}
/// <summary>
/// remove all cells except those that are adjacent to the empty cells
/// </summary>
private List<VoronoiCell> CleanCells(List<VoronoiCell> emptyCells)
{
List<VoronoiCell> newCells = new List<VoronoiCell>();
foreach (VoronoiCell cell in emptyCells)
{
foreach (GraphEdge edge in cell.edges)
{
VoronoiCell adjacent = edge.AdjacentCell(cell);
if (adjacent != null && !newCells.Contains(adjacent))
{
newCells.Add(adjacent);
}
}
}
return newCells;
}
private void GenerateRuin(List<VoronoiCell> mainPath)
{
Vector2 ruinSize = new Vector2(Rand.Range(5000.0f, 8000.0f, false), Rand.Range(5000.0f, 8000.0f, false));
float ruinRadius = Math.Max(ruinSize.X, ruinSize.Y) * 0.5f;
Vector2 ruinPos = cells[Rand.Int(cells.Count, false)].Center;
int iter = 0;
while (mainPath.Any(p => Vector2.Distance(ruinPos, p.Center) < ruinRadius * 2.0f))
{
Vector2 weighedPathPos = ruinPos;
iter++;
foreach (VoronoiCell pathCell in mainPath)
{
float dist = Vector2.Distance(pathCell.Center, ruinPos);
if (dist > 10000.0f) continue;
Vector2 moveAmount = Vector2.Normalize(ruinPos - pathCell.Center) * 100000.0f / dist;
//if (weighedPathPos.Y + moveAmount.Y > borders.Bottom - ruinSize.X)
//{
// moveAmount.X = (Math.Abs(moveAmount.Y) + Math.Abs(moveAmount.X))*Math.Sign(moveAmount.X);
// moveAmount.Y = 0.0f;
//}
weighedPathPos += moveAmount;
}
ruinPos = weighedPathPos;
if (iter > 10000) break;
}
VoronoiCell closestPathCell = null;
float closestDist = 0.0f;
foreach (VoronoiCell pathCell in mainPath)
{
float dist = Vector2.Distance(pathCell.Center, ruinPos);
if (closestPathCell == null || dist < closestDist)
{
closestPathCell = pathCell;
closestDist = dist;
}
}
var ruin = new Ruin(closestPathCell, cells, new Rectangle((ruinPos - ruinSize * 0.5f).ToPoint(), ruinSize.ToPoint()));
ruins = new List<Ruin>();
ruins.Add(ruin);
ruin.RuinShapes.Sort((shape1, shape2) => shape2.DistanceFromEntrance.CompareTo(shape1.DistanceFromEntrance));
for (int i = 0; i < 4; i++)
{
positionsOfInterest.Add(new InterestingPosition(ruin.RuinShapes[i].Rect.Center.ToVector2(), PositionType.Ruin));
}
foreach (RuinShape ruinShape in ruin.RuinShapes)
{
var tooClose = GetTooCloseCells(ruinShape.Rect.Center.ToVector2(), Math.Max(ruinShape.Rect.Width, ruinShape.Rect.Height));
tooClose.ForEach(c =>
{
if (c.edges.Any(e => ruinShape.Rect.Contains(e.point1) || ruinShape.Rect.Contains(e.point2))) c.CellType = CellType.Empty;
});
}
}
public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float offsetFromWall = 10.0f)
{
if (!positionsOfInterest.Any()) return Size*0.5f;
Vector2 position = Vector2.Zero;
offsetFromWall = ConvertUnits.ToSimUnits(offsetFromWall);
int tries = 0;
do
{
Vector2 startPos = Level.Loaded.GetRandomInterestingPosition(true, spawnPosType, true);
startPos += Rand.Vector(Rand.Range(0.0f, randomSpread, false), false);
Vector2 endPos = startPos - Vector2.UnitY * Size.Y;
if (Submarine.PickBody(
ConvertUnits.ToSimUnits(startPos),
ConvertUnits.ToSimUnits(endPos),
null, Physics.CollisionLevel) != null)
{
position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.Normalize(startPos - endPos)*offsetFromWall;
break;
}
tries++;
if (tries == 10)
{
position = EndPosition - Vector2.UnitY * 300.0f;
}
} while (tries < 10);
return position;
}
public Vector2 GetRandomInterestingPosition(bool useSyncedRand, PositionType positionType, bool avoidSubs)
{
if (!positionsOfInterest.Any()) return Size * 0.5f;
var matchingPositions = positionsOfInterest.FindAll(p => positionType.HasFlag(p.PositionType));
if (avoidSubs)
{
foreach (Submarine sub in Submarine.Loaded)
{
float minDist = Math.Max(sub.Borders.Width, sub.Borders.Height);
matchingPositions.RemoveAll(p => Vector2.Distance(p.Position, sub.WorldPosition) < minDist);
}
}
if (!matchingPositions.Any())
{
return positionsOfInterest[Rand.Int(positionsOfInterest.Count, !useSyncedRand)].Position;
}
return matchingPositions[Rand.Int(matchingPositions.Count, !useSyncedRand)].Position;
}
public void Update(float deltaTime)
{
if (Submarine.MainSub != null)
{
WrappingWall.UpdateWallShift(Submarine.MainSub.WorldPosition, wrappingWalls);
}
if (Hull.renderer != null)
{
Hull.renderer.ScrollWater((float)deltaTime);
}
renderer.Update(deltaTime);
}
public void DrawFront(SpriteBatch spriteBatch)
{
if (renderer == null) return;
renderer.Draw(spriteBatch);
if (GameMain.DebugDraw)
{
foreach (InterestingPosition pos in positionsOfInterest)
{
Color color = Color.Yellow;
if (pos.PositionType == PositionType.Cave)
{
color = Color.DarkOrange;
}
else if (pos.PositionType == PositionType.Ruin)
{
color = Color.LightGray;
}
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X-15.0f, -pos.Position.Y-15.0f), new Vector2(30.0f, 30.0f), color, true);
}
}
}
public void DrawBack(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, BackgroundCreatureManager backgroundSpriteManager = null)
{
float brightness = MathHelper.Clamp(50.0f + (cam.Position.Y - Size.Y) / 2000.0f, 10.0f, 40.0f);
float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3;
GameMain.LightManager.AmbientLight = new Color(backgroundColor * (brightness / avgValue), 1.0f);
graphics.Clear(backgroundColor);
if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, backgroundSpriteManager);
}
public List<VoronoiCell> GetCells(Vector2 pos, int searchDepth = 2)
{
int gridPosX = (int)Math.Floor(pos.X / GridCellSize);
int gridPosY = (int)Math.Floor(pos.Y / GridCellSize);
int startX = Math.Max(gridPosX - searchDepth, 0);
int endX = Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1);
int startY = Math.Max(gridPosY - searchDepth, 0);
int endY = Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1);
List<VoronoiCell> cells = new List<VoronoiCell>();
for (int x = startX; x <= endX; x++)
{
for (int y = startY; y <= endY; y++)
{
foreach (VoronoiCell cell in cellGrid[x, y])
{
cells.Add(cell);
}
}
}
if (wrappingWalls == null) return cells;
for (int side = 0; side < 2; side++)
{
for (int n = 0; n < 2; n++)
{
if (wrappingWalls[side, n] == null) continue;
if (Vector2.Distance(wrappingWalls[side, n].MidPos, pos) > WrappingWall.WallWidth) continue;
foreach (VoronoiCell cell in wrappingWalls[side, n].Cells)
{
cells.Add(cell);
}
}
}
return cells;
}
private void Unload()
{
if (renderer!=null)
{
renderer.Dispose();
renderer = null;
}
if (ruins != null)
{
ruins.Clear();
ruins = null;
}
if (wrappingWalls!=null)
{
for (int side = 0; side < 2; side++)
{
for (int i = 0; i < 2; i++)
{
if (wrappingWalls[side, i] != null) wrappingWalls[side, i].Dispose();
}
}
wrappingWalls = null;
}
cells = null;
if (bodies != null)
{
bodies.Clear();
bodies = null;
}
loaded = null;
}
}
}