Added randomly generated ocean floors. Atm the bottom is too deep to reach in most levels and there's not much to explore down there, but the plan is to have some levels where the bottom or some of the bottom formations are reachable (because we need hydrothermal vents!).
Ice walls also use vertex colors now, could be used to add some cosmetic variety to the levels.
This commit is contained in:
@@ -3,7 +3,6 @@ using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Factories;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -16,7 +15,6 @@ namespace Barotrauma
|
||||
{
|
||||
//all entities are disabled after they reach this depth
|
||||
public const float MaxEntityDepth = -300000.0f;
|
||||
public const float MaxCameraDepth = -290000.0f;
|
||||
|
||||
public const float ShaftHeight = 1000.0f;
|
||||
|
||||
@@ -53,7 +51,7 @@ namespace Barotrauma
|
||||
public const int GridCellSize = 2000;
|
||||
private List<VoronoiCell>[,] cellGrid;
|
||||
|
||||
//private WrappingWall[,] wrappingWalls;
|
||||
private LevelWall[] extraWalls;
|
||||
|
||||
//private float shaftHeight;
|
||||
|
||||
@@ -76,6 +74,7 @@ namespace Barotrauma
|
||||
|
||||
private LevelGenerationParams generationParams;
|
||||
|
||||
|
||||
public Vector2 StartPosition
|
||||
{
|
||||
get { return startPosition; }
|
||||
@@ -91,15 +90,27 @@ namespace Barotrauma
|
||||
get { return endPosition; }
|
||||
}
|
||||
|
||||
public float BottomPos
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float SeaFloorTopPos
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Ruin> Ruins
|
||||
{
|
||||
get { return ruins; }
|
||||
}
|
||||
|
||||
//public WrappingWall[,] WrappingWalls
|
||||
//{
|
||||
// get { return wrappingWalls; }
|
||||
//}
|
||||
public LevelWall[] WrappingWalls
|
||||
{
|
||||
get { return extraWalls; }
|
||||
}
|
||||
|
||||
public string Seed
|
||||
{
|
||||
@@ -112,12 +123,19 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public Body ShaftBody
|
||||
public Body TopBarrier
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Body BottomBarrier
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public LevelGenerationParams GenerationParams
|
||||
{
|
||||
get { return generationParams; }
|
||||
@@ -446,24 +464,23 @@ namespace Barotrauma
|
||||
bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out triangles);
|
||||
|
||||
#if CLIENT
|
||||
List<VertexPositionTexture> bodyVertices = CaveGenerator.GenerateRenderVerticeList(triangles);
|
||||
|
||||
renderer.SetBodyVertices(bodyVertices.ToArray());
|
||||
renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells));
|
||||
renderer.SetBodyVertices(CaveGenerator.GenerateRenderVerticeList(triangles).ToArray(), Color.White);
|
||||
renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells), Color.White);
|
||||
|
||||
renderer.PlaceSprites(generationParams.BackgroundSpriteAmount);
|
||||
#endif
|
||||
|
||||
ShaftBody = BodyFactory.CreateEdge(GameMain.World,
|
||||
TopBarrier = 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;
|
||||
TopBarrier.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, borders.Height)), 0.0f);
|
||||
TopBarrier.BodyType = BodyType.Static;
|
||||
TopBarrier.CollisionCategories = Physics.CollisionLevel;
|
||||
|
||||
bodies.Add(ShaftBody);
|
||||
bodies.Add(TopBarrier);
|
||||
|
||||
GenerateSeaFloor();
|
||||
|
||||
foreach (VoronoiCell cell in cells)
|
||||
{
|
||||
@@ -534,21 +551,9 @@ namespace Barotrauma
|
||||
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);
|
||||
@@ -567,8 +572,6 @@ namespace Barotrauma
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//prevWaypoint = newWaypoint;
|
||||
}
|
||||
|
||||
newWaypoint = new WayPoint(new Rectangle((int)pathCells[pathCells.Count - 1].Center.X, borders.Height, 10, 10), null);
|
||||
@@ -666,9 +669,57 @@ namespace Barotrauma
|
||||
return newCells;
|
||||
}
|
||||
|
||||
private void GenerateSeaFloor()
|
||||
{
|
||||
BottomPos = generationParams.SeaFloorDepth;
|
||||
SeaFloorTopPos = BottomPos;
|
||||
|
||||
List<Vector2> bottomPositions = new List<Vector2>();
|
||||
bottomPositions.Add(new Vector2(0, BottomPos));
|
||||
|
||||
int mountainCount = Rand.Range(generationParams.MountainCountMin, generationParams.MountainCountMax, Rand.RandSync.Server);
|
||||
for (int i = 0; i < mountainCount; i++)
|
||||
{
|
||||
bottomPositions.Add(
|
||||
new Vector2(Size.X / (mountainCount + 1) * (i + 1),
|
||||
BottomPos + Rand.Range(generationParams.MountainHeightMin, generationParams.MountainHeightMax, Rand.RandSync.Server)));
|
||||
}
|
||||
bottomPositions.Add(new Vector2(Size.X, BottomPos));
|
||||
|
||||
|
||||
float minVertexInterval = 5000.0f;
|
||||
float currInverval = Size.X / 2.0f;
|
||||
while (currInverval > minVertexInterval)
|
||||
{
|
||||
for (int i = 0; i < bottomPositions.Count - 1; i++)
|
||||
{
|
||||
bottomPositions.Insert(i+1,
|
||||
(bottomPositions[i] + bottomPositions[i + 1]) / 2.0f +
|
||||
Vector2.UnitY * Rand.Range(0.0f, generationParams.SeaFloorVariance, Rand.RandSync.Server));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
currInverval /= 2.0f;
|
||||
}
|
||||
|
||||
SeaFloorTopPos = bottomPositions.Max(p => p.Y);
|
||||
|
||||
extraWalls = new LevelWall[] { new LevelWall(bottomPositions, new Vector2(0.0f, -2000.0f), backgroundColor) };
|
||||
|
||||
BottomBarrier = BodyFactory.CreateEdge(GameMain.World,
|
||||
ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)),
|
||||
ConvertUnits.ToSimUnits(new Vector2(borders.Right, 0)));
|
||||
|
||||
BottomBarrier.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, BottomPos)), 0.0f);
|
||||
BottomBarrier.BodyType = BodyType.Static;
|
||||
BottomBarrier.CollisionCategories = Physics.CollisionLevel;
|
||||
|
||||
bodies.Add(BottomBarrier);
|
||||
}
|
||||
|
||||
private void GenerateRuin(List<VoronoiCell> mainPath)
|
||||
{
|
||||
|
||||
Vector2 ruinSize = new Vector2(Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server), Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server));
|
||||
float ruinRadius = Math.Max(ruinSize.X, ruinSize.Y) * 0.5f;
|
||||
|
||||
@@ -893,20 +944,16 @@ namespace Barotrauma
|
||||
ruins = null;
|
||||
}
|
||||
|
||||
/*
|
||||
if (wrappingWalls!=null)
|
||||
|
||||
if (extraWalls != null)
|
||||
{
|
||||
for (int side = 0; side < 2; side++)
|
||||
foreach (LevelWall w in extraWalls)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (wrappingWalls[side, i] != null) wrappingWalls[side, i].Dispose();
|
||||
}
|
||||
w.Dispose();
|
||||
}
|
||||
|
||||
wrappingWalls = null;
|
||||
}*/
|
||||
|
||||
extraWalls = null;
|
||||
}
|
||||
|
||||
cells = null;
|
||||
|
||||
|
||||
@@ -36,6 +36,15 @@ namespace Barotrauma
|
||||
//if 1.0f, the bottom will be completely open
|
||||
private float bottomHoleProbability;
|
||||
|
||||
//the y-position of the ocean floor (= the position from which the bottom formations extend upwards)
|
||||
private float seaFloorBaseDepth;
|
||||
//how much random variance there can be in the height of the formations
|
||||
private float seaFloorVariance;
|
||||
|
||||
private int mountainCountMin, mountainCountMax;
|
||||
|
||||
private float mountainHeightMin, mountainHeightMax;
|
||||
|
||||
private int ruinCount;
|
||||
|
||||
public Color BackgroundColor
|
||||
@@ -74,9 +83,10 @@ namespace Barotrauma
|
||||
public Vector2 VoronoiSiteInterval
|
||||
{
|
||||
get { return voronoiSiteInterval; }
|
||||
set {
|
||||
voronoiSiteInterval.X = MathHelper.Clamp(value.X, 100.0f, width/2);
|
||||
voronoiSiteInterval.Y = MathHelper.Clamp(value.Y, 100.0f, height/2);
|
||||
set
|
||||
{
|
||||
voronoiSiteInterval.X = MathHelper.Clamp(value.X, 100.0f, width / 2);
|
||||
voronoiSiteInterval.Y = MathHelper.Clamp(value.Y, 100.0f, height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +128,60 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(-300000.0f, false)]
|
||||
public float SeaFloorDepth
|
||||
{
|
||||
get { return seaFloorBaseDepth; }
|
||||
set { seaFloorBaseDepth = MathHelper.Clamp(value, Level.MaxEntityDepth, 0.0f); }
|
||||
}
|
||||
|
||||
[HasDefaultValue(1000.0f, false)]
|
||||
public float SeaFloorVariance
|
||||
{
|
||||
get { return seaFloorVariance; }
|
||||
set { seaFloorVariance = value; }
|
||||
}
|
||||
|
||||
[HasDefaultValue(0, false)]
|
||||
public int MountainCountMin
|
||||
{
|
||||
get { return mountainCountMin; }
|
||||
set
|
||||
{
|
||||
mountainCountMin = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(0, false)]
|
||||
public int MountainCountMax
|
||||
{
|
||||
get { return mountainCountMax; }
|
||||
set
|
||||
{
|
||||
mountainCountMax = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(1000.0f, false)]
|
||||
public float MountainHeightMin
|
||||
{
|
||||
get { return mountainHeightMin; }
|
||||
set
|
||||
{
|
||||
mountainHeightMin = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(5000.0f, false)]
|
||||
public float MountainHeightMax
|
||||
{
|
||||
get { return mountainHeightMax; }
|
||||
set
|
||||
{
|
||||
mountainHeightMax = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(1, false)]
|
||||
public int RuinCount
|
||||
{
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class LevelWall : IDisposable
|
||||
{
|
||||
private List<VoronoiCell> cells;
|
||||
|
||||
public List<VoronoiCell> Cells
|
||||
{
|
||||
get { return cells; }
|
||||
}
|
||||
|
||||
private List<Body> bodies;
|
||||
|
||||
public LevelWall(List<Vector2> edgePositions, Vector2 extendAmount, Color color)
|
||||
{
|
||||
cells = new List<VoronoiCell>();
|
||||
|
||||
for (int i = 0; i < edgePositions.Count - 1; i++)
|
||||
{
|
||||
Vector2[] vertices = new Vector2[4];
|
||||
vertices[0] = edgePositions[i];
|
||||
vertices[1] = edgePositions[i + 1];
|
||||
vertices[2] = vertices[0] + extendAmount;
|
||||
vertices[3] = vertices[1] + extendAmount;
|
||||
|
||||
VoronoiCell wallCell = new VoronoiCell(vertices);
|
||||
wallCell.edges[0].cell1 = wallCell;
|
||||
wallCell.edges[1].cell1 = wallCell;
|
||||
wallCell.edges[2].cell1 = wallCell;
|
||||
wallCell.edges[3].cell1 = wallCell;
|
||||
|
||||
wallCell.edges[0].isSolid = true;
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
wallCell.edges[3].cell2 = cells[i - 1];
|
||||
cells[i - 1].edges[1].cell2 = wallCell;
|
||||
}
|
||||
|
||||
cells.Add(wallCell);
|
||||
}
|
||||
|
||||
List<Vector2[]> triangles;
|
||||
bodies = CaveGenerator.GeneratePolygons(cells, out triangles, false);
|
||||
|
||||
#if CLIENT
|
||||
List<VertexPositionTexture> bodyVertices = CaveGenerator.GenerateRenderVerticeList(triangles);
|
||||
|
||||
SetBodyVertices(bodyVertices.ToArray(), color);
|
||||
SetWallVertices(CaveGenerator.GenerateWallShapes(cells), color);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
#if CLIENT
|
||||
if (wallVertices != null)
|
||||
{
|
||||
wallVertices.Dispose();
|
||||
wallVertices = null;
|
||||
}
|
||||
if (bodyVertices != null)
|
||||
{
|
||||
BodyVertices.Dispose();
|
||||
bodyVertices = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bodies != null)
|
||||
{
|
||||
bodies.Clear();
|
||||
bodies = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class WrappingWall : IDisposable
|
||||
{
|
||||
public const float WallWidth = 20000.0f;
|
||||
|
||||
private Vector2 midPos;
|
||||
private int slot;
|
||||
|
||||
private Vector2 offset;
|
||||
|
||||
private List<VoronoiCell> cells;
|
||||
|
||||
public Vector2 Offset
|
||||
{
|
||||
get { return offset; }
|
||||
}
|
||||
|
||||
public List<VoronoiCell> Cells
|
||||
{
|
||||
get { return cells; }
|
||||
}
|
||||
|
||||
public Vector2 MidPos
|
||||
{
|
||||
get { return midPos; }
|
||||
}
|
||||
|
||||
public WrappingWall(List<VoronoiCell> pathCells, List<VoronoiCell> mapCells, Rectangle ignoredArea, int dir = -1)
|
||||
{
|
||||
cells = new List<VoronoiCell>();
|
||||
|
||||
VoronoiCell edgeCell = null;
|
||||
foreach (VoronoiCell cell in mapCells)
|
||||
{
|
||||
if (ignoredArea.Contains(cell.Center)) continue;
|
||||
if (Math.Sign(cell.Center.X - ignoredArea.Center.X) != Math.Sign(dir)) continue;
|
||||
if (edgeCell == null || cell.Center.Y < edgeCell.Center.Y)
|
||||
{
|
||||
edgeCell = cell;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 wallSectionSize = new Vector2(2000.0f, 2000.0f);
|
||||
Vector2 startPos = (dir < 0) ?
|
||||
edgeCell.Center + Vector2.UnitX * WallWidth * dir :
|
||||
edgeCell.Center + WallWidth * Vector2.UnitX * (dir - 1);
|
||||
|
||||
midPos = startPos + Vector2.UnitX * WallWidth/2;
|
||||
|
||||
List<Vector2> bottomVertices = new List<Vector2>();
|
||||
|
||||
for (float x = 0; x <= WallWidth; x += wallSectionSize.X)
|
||||
{
|
||||
Vector2 center = new Vector2(startPos.X + x, edgeCell.Center.Y);
|
||||
float distFromCenter = Math.Abs(x - WallWidth / 2);
|
||||
float distFromEdge = WallWidth / 2 - distFromCenter;
|
||||
float normalizedDist = distFromEdge / (WallWidth / 2);
|
||||
|
||||
float variance = 1000.0f * normalizedDist;
|
||||
bottomVertices.Add(center + new Vector2(Rand.Range(-variance, variance, Rand.RandSync.Server), Rand.Range(-variance, variance, Rand.RandSync.Server) *2.0f));
|
||||
}
|
||||
|
||||
for (int i = 1; i < bottomVertices.Count; i++)
|
||||
{
|
||||
Vector2[] vertices = new Vector2[4];
|
||||
vertices[0] = bottomVertices[i];
|
||||
vertices[1] = bottomVertices[i - 1];
|
||||
vertices[2] = vertices[1] + Vector2.UnitY * wallSectionSize.Y;
|
||||
vertices[3] = vertices[0] + Vector2.UnitY * wallSectionSize.Y;
|
||||
|
||||
VoronoiCell wallCell = new VoronoiCell(vertices);
|
||||
wallCell.edges[0].cell1 = wallCell;
|
||||
wallCell.edges[1].cell1 = wallCell;
|
||||
wallCell.edges[2].cell1 = wallCell;
|
||||
wallCell.edges[3].cell1 = wallCell;
|
||||
|
||||
wallCell.edges[0].isSolid = true;
|
||||
wallCell.edges[2].isSolid = true;
|
||||
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
wallCell.edges[1].cell2 = cells[i - 2];
|
||||
cells[i - 2].edges[3].cell2 = wallCell;
|
||||
}
|
||||
|
||||
cells.Add(wallCell);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateWallShift(Vector2 pos, WrappingWall[,] walls)
|
||||
{
|
||||
if (pos.X < walls[0, 1].midPos.X && walls[0,0].midPos.X > pos.X)
|
||||
{
|
||||
walls[0, 0].Shift(-2);
|
||||
|
||||
var temp = walls[0, 0];
|
||||
walls[0, 0] = walls[0, 1];
|
||||
walls[0, 1] = temp;
|
||||
}
|
||||
else if (pos.X > walls[0, 0].midPos.X && walls[0,1].midPos.X < pos.X && walls[0,1].slot<0)
|
||||
{
|
||||
walls[0, 1].Shift(2);
|
||||
|
||||
var temp = walls[0, 0];
|
||||
walls[0, 0] = walls[0, 1];
|
||||
walls[0, 1] = temp;
|
||||
}
|
||||
else if (pos.X > walls[1, 1].midPos.X && walls[1,0].midPos.X < pos.X)
|
||||
{
|
||||
walls[1, 0].Shift(2);
|
||||
|
||||
var temp = walls[1, 0];
|
||||
walls[1, 0] = walls[1, 1];
|
||||
walls[1, 1] = temp;
|
||||
}
|
||||
else if (pos.X < walls[1, 0].midPos.X && walls[1, 1].midPos.X > pos.X && walls[1, 1].slot > 0)
|
||||
{
|
||||
walls[1, 1].Shift(-2);
|
||||
|
||||
var temp = walls[0, 0];
|
||||
walls[1, 0] = walls[1, 1];
|
||||
walls[1, 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public void Shift(int amount)
|
||||
{
|
||||
slot += amount;
|
||||
|
||||
Vector2 moveAmount = Vector2.UnitX * WallWidth * amount;
|
||||
Vector2 simMoveAmount = ConvertUnits.ToSimUnits(moveAmount);
|
||||
|
||||
foreach (VoronoiCell cell in cells)
|
||||
{
|
||||
cell.body.SetTransform(cell.body.Position + simMoveAmount, 0.0f);
|
||||
cell.Translation += moveAmount;
|
||||
}
|
||||
|
||||
midPos += moveAmount;
|
||||
offset += moveAmount;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
#if CLIENT
|
||||
if (wallVertices != null)
|
||||
{
|
||||
wallVertices.Dispose();
|
||||
wallVertices = null;
|
||||
}
|
||||
if (bodyVertices != null)
|
||||
{
|
||||
bodyVertices.Dispose();
|
||||
bodyVertices = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user