diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 7c395ab6e..ece15e6e2 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -137,6 +137,7 @@ + @@ -347,6 +348,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -767,6 +777,10 @@ PreserveNewest + + PreserveNewest + Designer + PreserveNewest diff --git a/Subsurface/Content/BackgroundSprites/BackgroundSpritePrefabs.xml b/Subsurface/Content/BackgroundSprites/BackgroundSpritePrefabs.xml index be3735b03..033a80b77 100644 --- a/Subsurface/Content/BackgroundSprites/BackgroundSpritePrefabs.xml +++ b/Subsurface/Content/BackgroundSprites/BackgroundSpritePrefabs.xml @@ -1,34 +1,86 @@  - + - + + - + + + + - + + + + + + - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/BackgroundSprites/vegetation.png b/Subsurface/Content/BackgroundSprites/vegetation.png index 9c4c1937b..6c5f4d0a4 100644 Binary files a/Subsurface/Content/BackgroundSprites/vegetation.png and b/Subsurface/Content/BackgroundSprites/vegetation.png differ diff --git a/Subsurface/Content/BackgroundSprites/vegetation2.png b/Subsurface/Content/BackgroundSprites/vegetation2.png index c0383a41d..2216ad886 100644 Binary files a/Subsurface/Content/BackgroundSprites/vegetation2.png and b/Subsurface/Content/BackgroundSprites/vegetation2.png differ diff --git a/Subsurface/Content/BackgroundSprites/vegetation3.png b/Subsurface/Content/BackgroundSprites/vegetation3.png new file mode 100644 index 000000000..921c0939f Binary files /dev/null and b/Subsurface/Content/BackgroundSprites/vegetation3.png differ diff --git a/Subsurface/Content/BackgroundSprites/vegetation4.png b/Subsurface/Content/BackgroundSprites/vegetation4.png new file mode 100644 index 000000000..6aebec217 Binary files /dev/null and b/Subsurface/Content/BackgroundSprites/vegetation4.png differ diff --git a/Subsurface/Content/BackgroundSprites/vegetation5.png b/Subsurface/Content/BackgroundSprites/vegetation5.png new file mode 100644 index 000000000..789a107a4 Binary files /dev/null and b/Subsurface/Content/BackgroundSprites/vegetation5.png differ diff --git a/Subsurface/Content/Map/LevelGenerationParameters.xml b/Subsurface/Content/Map/LevelGenerationParameters.xml new file mode 100644 index 000000000..add72c487 --- /dev/null +++ b/Subsurface/Content/Map/LevelGenerationParameters.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs index 67521f327..d6ff2000c 100644 --- a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs +++ b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs @@ -35,10 +35,11 @@ namespace Barotrauma const int GridSize = 1000; private List prefabs = new List(); - - + private List[,] sprites; + private float swingTimer; + public BackgroundSpriteManager(string configPath) { LoadConfig(configPath); @@ -83,38 +84,37 @@ namespace Barotrauma for (int i = 0 ; i < amount; i++) { - BackgroundSpritePrefab prefab = GetRandomPrefab(); + BackgroundSpritePrefab prefab = GetRandomPrefab(level.GenerationParams.Name); GraphEdge selectedEdge = null; - Vector2? pos = FindSpritePosition(level, prefab, out selectedEdge); + Vector2 edgeNormal = Vector2.One; + Vector2? pos = FindSpritePosition(level, prefab, out selectedEdge, out edgeNormal); if (pos == null) continue; float rotation = 0.0f; if (prefab.AlignWithSurface) { - Vector2 leftPoint = selectedEdge.point2; - Vector2 rightPoint = selectedEdge.point1; - - rotation = -MathUtils.VectorToAngle(rightPoint - leftPoint); + rotation = MathUtils.VectorToAngle(new Vector2(edgeNormal.Y, edgeNormal.X)); } rotation += Rand.Range(prefab.RandomRotation.X, prefab.RandomRotation.Y, false); var newSprite = new BackgroundSprite(prefab, (Vector2)pos, Rand.Range(prefab.Scale.X, prefab.Scale.Y, false), rotation); - + int x = (int)Math.Floor(((Vector2)pos).X / GridSize); - if (x<0 || x >= sprites.GetLength(0)) continue; + if (x < 0 || x >= sprites.GetLength(0)) continue; int y = (int)Math.Floor(((Vector2)pos).Y / GridSize); - if (y<0 || y >= sprites.GetLength(1)) continue; + if (y < 0 || y >= sprites.GetLength(1)) continue; sprites[x,y].Add(newSprite); } } - private Vector2? FindSpritePosition(Level level, BackgroundSpritePrefab prefab, out GraphEdge closestEdge) + private Vector2? FindSpritePosition(Level level, BackgroundSpritePrefab prefab, out GraphEdge closestEdge, out Vector2 edgeNormal) { closestEdge = null; + edgeNormal = Vector2.One; Vector2 randomPos = new Vector2( Rand.Range(0.0f, level.Size.X, false), @@ -126,68 +126,67 @@ namespace Barotrauma VoronoiCell cell = cells[Rand.Int(cells.Count, false)]; List edges = new List(); + List normals = new List(); foreach (GraphEdge edge in cell.edges) { - if (!edge.isSolid) continue; + if (!edge.isSolid || edge.OutsideLevel) continue; + + Vector2 normal = edge.GetNormal(cell); - if (prefab.Alignment.HasFlag(Alignment.Bottom)) - { - if (Math.Abs(edge.point1.X - edge.point2.X) < Math.Abs(edge.point1.Y - edge.point2.Y)) continue; - if (edge.Center.Y < cell.Center.Y) edges.Add(edge); - } - else if (prefab.Alignment.HasFlag(Alignment.Top)) - { - if (Math.Abs(edge.point1.X - edge.point2.X) < Math.Abs(edge.point1.Y - edge.point2.Y)) continue; - if (edge.Center.Y > cell.Center.Y) edges.Add(edge); - } - else if (prefab.Alignment.HasFlag(Alignment.Left)) - { - if (edge.Center.X < cell.Center.X) edges.Add(edge); - } - else if (prefab.Alignment.HasFlag(Alignment.Right)) - { - if (edge.Center.X > cell.Center.X) edges.Add(edge); - } - else + if (prefab.Alignment.HasFlag(Alignment.Bottom) && normal.Y < -0.5f) { edges.Add(edge); } + else if (prefab.Alignment.HasFlag(Alignment.Top) && normal.Y > 0.5f) + { + edges.Add(edge); + } + else if (prefab.Alignment.HasFlag(Alignment.Left) && normal.X < -0.5f) + { + edges.Add(edge); + } + else if (prefab.Alignment.HasFlag(Alignment.Right) && normal.X > 0.5f) + { + edges.Add(edge); + } + else + { + continue; + } + + normals.Add(normal); } if (!edges.Any()) return null; - closestEdge = edges[Rand.Int(edges.Count,false)]; + int index = Rand.Int(edges.Count,false); + closestEdge = edges[index]; + edgeNormal = normals[index]; float length = Vector2.Distance(closestEdge.point1, closestEdge.point2); Vector2 dir = (closestEdge.point1 - closestEdge.point2) / length; - Vector2 pos = closestEdge.Center; - - pos = closestEdge.point2 + dir * Rand.Range(prefab.Sprite.size.X / 2.0f, length - prefab.Sprite.size.X / 2.0f, false); - - if (prefab.Alignment.HasFlag(Alignment.Top)) - { - pos.Y -= Math.Abs(dir.Y) * prefab.Sprite.size.X / Math.Abs(dir.X); - } - else if (prefab.Alignment.HasFlag(Alignment.Bottom)) - { - pos.Y += Math.Abs(dir.Y) * prefab.Sprite.size.X / Math.Abs(dir.X); - } - + Vector2 pos = closestEdge.point2 + dir * Rand.Range(prefab.Sprite.size.X / 2.0f, length - prefab.Sprite.size.X / 2.0f, false); + return pos; } + public void Update(float deltaTime) + { + swingTimer += deltaTime; + } + public void DrawSprites(SpriteBatch spriteBatch, Camera cam) { Rectangle indices = Rectangle.Empty; - indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize) - 1; + indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize) - 2; if (indices.X >= sprites.GetLength(0)) return; - indices.Y = (int)Math.Floor((cam.WorldView.Y - cam.WorldView.Height) / (float)GridSize) - 1; + indices.Y = (int)Math.Floor((cam.WorldView.Y - cam.WorldView.Height) / (float)GridSize) - 2; if (indices.Y >= sprites.GetLength(1)) return; - indices.Width = (int)Math.Ceiling(cam.WorldView.Right / (float)GridSize) + 1; + indices.Width = (int)Math.Ceiling(cam.WorldView.Right / (float)GridSize) + 2; if (indices.Width < 0) return; - indices.Height = (int)Math.Ceiling(cam.WorldView.Y / (float)GridSize) + 1; + indices.Height = (int)Math.Ceiling(cam.WorldView.Y / (float)GridSize) + 2; if (indices.Height < 0) return; indices.X = Math.Max(indices.X, 0); @@ -195,6 +194,8 @@ namespace Barotrauma indices.Width = Math.Min(indices.Width, sprites.GetLength(0)); indices.Height = Math.Min(indices.Height, sprites.GetLength(1)); + float swingState = (float)Math.Sin(swingTimer * 0.1f); + float z = 0.0f; for (int x = indices.X; x < indices.Width; x++) { @@ -202,31 +203,44 @@ namespace Barotrauma { foreach (BackgroundSprite sprite in sprites[x, y]) { - sprite.Prefab.Sprite.Draw(spriteBatch, new Vector2(sprite.Position.X, -sprite.Position.Y), Color.White, sprite.Rotation, sprite.Scale, SpriteEffects.None, z); + sprite.Prefab.Sprite.Draw( + spriteBatch, + new Vector2(sprite.Position.X, -sprite.Position.Y), + Color.White, + sprite.Rotation + swingState*sprite.Prefab.SwingAmount, + sprite.Scale, + SpriteEffects.None, + z); + + if (GameMain.DebugDraw) + { + GUI.DrawRectangle(spriteBatch, new Vector2(sprite.Position.X, -sprite.Position.Y), new Vector2(10.0f, 10.0f), Color.Red, true); + } + z += 0.0001f; } } } } - private BackgroundSpritePrefab GetRandomPrefab() + private BackgroundSpritePrefab GetRandomPrefab(string levelType) { int totalCommonness = 0; foreach (BackgroundSpritePrefab prefab in prefabs) { - totalCommonness += prefab.Commonness; + totalCommonness += prefab.GetCommonness(levelType); } float randomNumber = Rand.Int(totalCommonness+1, false); foreach (BackgroundSpritePrefab prefab in prefabs) { - if (randomNumber <= prefab.Commonness) + if (randomNumber <= prefab.GetCommonness(levelType)) { return prefab; } - randomNumber -= prefab.Commonness; + randomNumber -= prefab.GetCommonness(levelType); } return null; diff --git a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpritePrefab.cs b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpritePrefab.cs index c6c3b7ffd..3f98f6206 100644 --- a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpritePrefab.cs +++ b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpritePrefab.cs @@ -1,5 +1,6 @@ using Microsoft.Xna.Framework; using System; +using System.Collections.Generic; using System.Xml.Linq; namespace Barotrauma @@ -16,13 +17,20 @@ namespace Barotrauma public readonly Vector2 RandomRotation; + public readonly float SwingAmount; + public readonly int Commonness; + public Dictionary OverrideCommonness; + public BackgroundSpritePrefab(XElement element) { - string alignmentStr = ToolBox.GetAttributeString(element, "alignment", "BottomCenter"); + string alignmentStr = ToolBox.GetAttributeString(element, "alignment", ""); - if (!Enum.TryParse(alignmentStr, out Alignment)) Alignment = Alignment.BottomCenter; + if (string.IsNullOrEmpty(alignmentStr) || !Enum.TryParse(alignmentStr, out Alignment)) + { + Alignment = Alignment.Top | Alignment.Bottom | Alignment.Left | Alignment.Right; + } Commonness = ToolBox.GetAttributeInt(element, "commonness", 1); @@ -35,14 +43,39 @@ namespace Barotrauma RandomRotation.X = MathHelper.ToRadians(RandomRotation.X); RandomRotation.Y = MathHelper.ToRadians(RandomRotation.Y); + SwingAmount = MathHelper.ToRadians(ToolBox.GetAttributeFloat(element, "swingamount", 0.0f)); + + OverrideCommonness = new Dictionary(); + foreach (XElement subElement in element.Elements()) { - if (subElement.Name.ToString().ToLowerInvariant() != "sprite") continue; + switch(subElement.Name.ToString().ToLowerInvariant()) + { + case "sprite": + Sprite = new Sprite(subElement); + break; + case "overridecommonness": + string levelType = ToolBox.GetAttributeString(subElement, "leveltype", ""); + if (!OverrideCommonness.ContainsKey(levelType)) + { + OverrideCommonness.Add(levelType, ToolBox.GetAttributeInt(subElement, "commonness", 1)); + } + break; - Sprite = new Sprite(subElement); - break; + } } } + public int GetCommonness(string levelType) + { + int commonness = 0; + if (!OverrideCommonness.TryGetValue(levelType, out commonness)) + { + return Commonness; + } + + return commonness; + } + } } diff --git a/Subsurface/Source/ContentPackage.cs b/Subsurface/Source/ContentPackage.cs index 42f314c73..f7d5bca65 100644 --- a/Subsurface/Source/ContentPackage.cs +++ b/Subsurface/Source/ContentPackage.cs @@ -10,7 +10,17 @@ namespace Barotrauma { public enum ContentType { - None, Jobs, Item, Character, Structure, Executable, LocationTypes, RandomEvents, Missions, BackgroundCreaturePrefabs, BackgroundSpritePrefabs + None, + Jobs, + Item, + Character, + Structure, + Executable, + LocationTypes, + LevelGenerationPresets, + RandomEvents, + Missions, + BackgroundCreaturePrefabs, BackgroundSpritePrefabs } public class ContentPackage diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 573ee3828..ee2414876 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -495,10 +495,6 @@ namespace Barotrauma case "fire": if (GameMain.Client == null) Hull.EditFire = !Hull.EditFire; - break; - case "generatelevel": - GameMain.Level = new Level("asdf", 50.0f, 500,500, 50); - GameMain.Level.Generate(); break; case "fixitems": foreach (Item it in Item.ItemList) diff --git a/Subsurface/Source/GameMain.cs b/Subsurface/Source/GameMain.cs index 15a9555af..a10bacd6b 100644 --- a/Subsurface/Source/GameMain.cs +++ b/Subsurface/Source/GameMain.cs @@ -218,6 +218,7 @@ namespace Barotrauma Mission.Init(); MapEntityPrefab.Init(); + LevelGenerationParams.LoadPresets(); TitleScreen.LoadState = 10.0f; yield return CoroutineStatus.Running; diff --git a/Subsurface/Source/Map/Levels/CaveGenerator.cs b/Subsurface/Source/Map/Levels/CaveGenerator.cs index 0602aa5e6..e1836f09d 100644 --- a/Subsurface/Source/Map/Levels/CaveGenerator.cs +++ b/Subsurface/Source/Map/Levels/CaveGenerator.cs @@ -189,16 +189,18 @@ namespace Barotrauma { Site site = (i == 0) ? ge.site1 : ge.site2; - VoronoiCell cell = cellGrid[ - (int)Math.Floor((site.coord.x-borders.X) / gridCellSize), - (int)Math.Floor((site.coord.y-borders.Y) / gridCellSize)].Find(c => c.site == site); + int x = (int)(Math.Floor((site.coord.x-borders.X) / gridCellSize)); + int y = (int)(Math.Floor((site.coord.y-borders.Y) / gridCellSize)); + + x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0)-1); + y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1)-1); + + VoronoiCell cell = cellGrid[x,y].Find(c => c.site == site); if (cell == null) { cell = new VoronoiCell(site); - cellGrid[ - (int)Math.Floor((cell.Center.X-borders.X) / gridCellSize), - (int)Math.Floor((cell.Center.Y - borders.Y) / gridCellSize)].Add(cell); + cellGrid[x, y].Add(cell); cells.Add(cell); } diff --git a/Subsurface/Source/Map/Levels/Level.cs b/Subsurface/Source/Map/Levels/Level.cs index d60196be0..6996aeb24 100644 --- a/Subsurface/Source/Map/Levels/Level.cs +++ b/Subsurface/Source/Map/Levels/Level.cs @@ -50,9 +50,7 @@ namespace Barotrauma public const float ExitDistance = 6000.0f; private string seed; - - private int siteInterval; - + public const int GridCellSize = 2000; private List[,] cellGrid; @@ -77,6 +75,8 @@ namespace Barotrauma private Color backgroundColor; + private LevelGenerationParams generationParams; + public Vector2 StartPosition { get { return startPosition; } @@ -119,29 +119,32 @@ namespace Barotrauma private set; } + public LevelGenerationParams GenerationParams + { + get { return generationParams; } + } + public Color BackgroundColor { get { return backgroundColor; } } - public Level(string seed, float difficulty, int width, int height, int siteInterval) + public Level(string seed, float difficulty, LevelGenerationParams generationParams) { this.seed = seed; - - this.siteInterval = siteInterval; - + this.Difficulty = difficulty; - borders = new Rectangle(0, 0, width, height); + 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; - - Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); - - return new Level(seed, locationConnection.Difficulty, Rand.Range(80000, 120000, false), Rand.Range(40000, 60000, false), 2000); + + return new Level(seed, locationConnection.Difficulty, LevelGenerationParams.GetRandom(seed)); } public static Level CreateRandom(string seed = "") @@ -153,10 +156,10 @@ namespace Barotrauma Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); - return new Level(seed, Rand.Range(30.0f, 80.0f, false), Rand.Range(80000, 120000, false), Rand.Range(40000, 60000, false), 2000); + return new Level(seed, Rand.Range(30.0f, 80.0f, false), LevelGenerationParams.GetRandom(seed)); } - public void Generate(bool mirror=false) + public void Generate(bool mirror = false) { Stopwatch sw = new Stopwatch(); sw.Start(); @@ -175,47 +178,50 @@ namespace Barotrauma bodies = new List(); Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); - - float brightness = Rand.Range(1.0f, 1.3f, false); - backgroundColor = Color.Lerp(new Color(11, 18, 26), new Color(50, 46, 20), Rand.Range(0.0f, 1.0f, false)) * brightness; - backgroundColor = new Color(backgroundColor, 1.0f); - + + backgroundColor = generationParams.BackgroundColor; float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3; GameMain.LightManager.AmbientLight = new Color(backgroundColor * (60.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(minWidth * 2, Rand.Range(minWidth * 2, borders.Height - minWidth * 2, false)); - endPosition = new Vector2(borders.Width - minWidth * 2, Rand.Range(minWidth * 2, borders.Height - minWidth * 2, false)); + 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 pathNodes = new List(); 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)); - pathNodes.Add(startPosition); + Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange; - for (float x = startPosition.X; x < endPosition.X; x += Rand.Range(5000.0f, 10000.0f, false)) + 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(endPosition); pathNodes.Add(new Vector2(endPosition.X, borders.Height)); - - int smallTunnelCount = 5; - + List> smallTunnels = new List>(); - for (int i = 0; i < smallTunnelCount; i++) + 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(5000.0f, 10000.0f, false); + float tunnelLength = Rand.Range( + generationParams.SmallTunnelLengthRange.X, + generationParams.SmallTunnelLengthRange.Y, + false); var tunnelNodes = MathUtils.GenerateJaggedLine( tunnelStartPos, @@ -231,21 +237,23 @@ namespace Barotrauma if (tunnel.Any()) smallTunnels.Add(tunnel); } - - - - float siteVariance = siteInterval * 0.4f; - for (int x = siteInterval / 2; x < borders.Width; x += siteInterval) + + Vector2 siteInterval = generationParams.VoronoiSiteInterval; + Vector2 siteVariance = generationParams.VoronoiSiteVariance; + for (float x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X) { - for (int y = siteInterval / 2; y < borders.Height; y += siteInterval) + for (float y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y) { - Vector2 site = new Vector2(x, y) + Rand.Vector(siteVariance, false); + 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))) + if (smallTunnels.Any(t => t.Any(node => Vector2.Distance(node, site) < siteInterval.Length()))) { - if (x < borders.Width - siteInterval) sites.Add(new Vector2(x, y) + Vector2.UnitX * siteInterval * 0.5f); - if (y < borders.Height - siteInterval) sites.Add(new Vector2(x, y) + Vector2.UnitY * siteInterval * 0.5f); - if (x < borders.Width - siteInterval && y < borders.Height - siteInterval) sites.Add(new Vector2(x, y) + Vector2.One * siteInterval * 0.5f); + //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; @@ -259,7 +267,6 @@ namespace Barotrauma List graphEdges = voronoi.MakeVoronoiGraph(sites, borders.Width, borders.Height); - Debug.WriteLine("MakeVoronoiGraph: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); @@ -270,8 +277,7 @@ namespace Barotrauma sw2.Restart(); List mainPath = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize, - new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.3f, mirror); - + new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.5f, mirror); for (int i = 2; i < mainPath.Count; i += 3) { @@ -315,12 +321,20 @@ namespace Barotrauma cells = CleanCells(pathCells); - pathCells.AddRange(CreateBottomHoles(Rand.Range(0.1f,0.8f, false), new Rectangle( + pathCells.AddRange(CreateBottomHoles(generationParams.BottomHoleProbability, new Rectangle( (int)(borders.Width * 0.2f), 0, - (int)(borders.Width * 0.6f), (int)(borders.Height * 0.3f)))); + (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); } @@ -408,92 +422,33 @@ namespace Barotrauma cellGrid[x, y].Add(cell); } - - 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)) + for (int i = 0; i 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(); - 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)); + GenerateRuin(mainPath); } startPosition.Y = borders.Height; endPosition.Y = borders.Height; List cellsWithBody = new List(cells); - 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; - }); - } - + List bodyVertices; bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out bodyVertices); renderer.SetBodyVertices(bodyVertices.ToArray()); renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells)); - renderer.PlaceSprites(1000); + 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, borders.Height * 0.5f, + wrappingWalls[side, i] = new WrappingWall(pathCells, cells, ignoredArea, (side == 0 ? -1 : 1) * (i + 1)); List wrappingWallVertices; @@ -532,27 +487,18 @@ namespace Barotrauma edge.site1 = null; edge.site2 = null; } - } Debug.WriteLine("Generatelevel: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); - - //vertexBuffer = new VertexBuffer(GameMain.CurrGraphicsDevice, VertexPositionTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); - //vertexBuffer.SetData(vertices); - + if (mirror) { Vector2 temp = startPosition; startPosition = endPosition; endPosition = temp; } - - - //RuinGeneration.RuinGenerator.Draw(spriteBatch); - - Debug.WriteLine("**********************************************************************************"); Debug.WriteLine("Generated a map with " + sites.Count + " sites in " + sw.ElapsedMilliseconds + " ms"); Debug.WriteLine("Seed: "+seed); @@ -569,15 +515,26 @@ namespace Barotrauma 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; - - //foreach (VoronoiCell cell in toBeRemoved) - //{ - // cells.Remove(cell); - //} } private void EnlargeMainPath(List pathCells, float minWidth) @@ -692,22 +649,7 @@ namespace Barotrauma if (tooClose && !tooCloseCells.Contains(cell)) tooCloseCells.Add(cell); } - - //for (float x = -minDistance; x <= minDistance; x+=siteInterval) - //{ - // for (float y = -minDistance; y <= minDistance; y += siteInterval) - // { - // Vector2 cornerPos = position + new Vector2(x,y); - - // int cellIndex = CaveGenerator.FindCellIndex(cornerPos, cells, cellGrid, GridCellSize); - // if (cellIndex == -1) continue; - // if (!tooCloseCells.Contains(cells[cellIndex])) - // { - // tooCloseCells.Add(cells[cellIndex]); - // } - // } - //} - + return tooCloseCells; } @@ -724,13 +666,86 @@ namespace Barotrauma foreach (GraphEdge edge in cell.edges) { VoronoiCell adjacent = edge.AdjacentCell(cell); - if (adjacent!=null && !newCells.Contains(adjacent)) newCells.Add(adjacent); + if (adjacent != null && !newCells.Contains(adjacent)) + { + newCells.Add(adjacent); + } } } return newCells; } + private void GenerateRuin(List 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(); + 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; @@ -889,7 +904,7 @@ namespace Barotrauma { for (int i = 0; i < 2; i++) { - wrappingWalls[side, i].Dispose(); + if (wrappingWalls[side, i] != null) wrappingWalls[side, i].Dispose(); } } diff --git a/Subsurface/Source/Map/Levels/LevelGenerationParams.cs b/Subsurface/Source/Map/Levels/LevelGenerationParams.cs new file mode 100644 index 000000000..644c894c6 --- /dev/null +++ b/Subsurface/Source/Map/Levels/LevelGenerationParams.cs @@ -0,0 +1,216 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Barotrauma +{ + class LevelGenerationParams : IPropertyObject + { + private static List presets; + + public string Name + { + get; + private set; + } + + private float width, height; + + private Vector2 voronoiSiteInterval; + //how much the sites are "scattered" on x- and y-axis + //if Vector2.Zero, the sites will just be placed in a regular grid pattern + private Vector2 voronoiSiteVariance; + + //how far apart the nodes of the main path can be + //x = min interval, y = max interval + private Vector2 mainPathNodeIntervalRange; + + private int smallTunnelCount; + //x = min length, y = max length + private Vector2 smallTunnelLengthRange; + + //how large portion of the bottom of the level should be "carved out" + //if 0.0f, the bottom will be completely solid (making the abyss unreachable) + //if 1.0f, the bottom will be completely open + private float bottomHoleProbability; + + private int ruinCount; + + public Color BackgroundColor + { + get; + set; + } + + [HasDefaultValue(1000, false)] + public int BackgroundSpriteAmount + { + get; + set; + } + + public Dictionary ObjectProperties + { + get; + set; + } + + [HasDefaultValue(100000.0f, false)] + public float Width + { + get { return width; } + set { width = Math.Max(value, 2000.0f); } + } + + [HasDefaultValue(50000.0f, false)] + public float Height + { + get { return height; } + set { height = Math.Max(value, 2000.0f); } + } + + 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); + } + } + + public Vector2 VoronoiSiteVariance + { + get { return voronoiSiteVariance; } + set + { + voronoiSiteVariance = new Vector2( + MathHelper.Clamp(value.X, 0, voronoiSiteInterval.X), + MathHelper.Clamp(value.Y, 0, voronoiSiteInterval.Y)); + } + } + + public Vector2 MainPathNodeIntervalRange + { + get { return mainPathNodeIntervalRange; } + set + { + mainPathNodeIntervalRange.X = MathHelper.Clamp(value.X, 100.0f, width / 2); + mainPathNodeIntervalRange.Y = MathHelper.Clamp(value.Y, mainPathNodeIntervalRange.X, width / 2); + } + } + + [HasDefaultValue(5, false)] + public int SmallTunnelCount + { + get { return smallTunnelCount; } + set { smallTunnelCount = MathHelper.Clamp(value, 0, 100); } + } + + public Vector2 SmallTunnelLengthRange + { + get { return smallTunnelLengthRange; } + set + { + smallTunnelLengthRange.X = MathHelper.Clamp(value.X, 100.0f, width); + smallTunnelLengthRange.Y = MathHelper.Clamp(value.Y, smallTunnelLengthRange.X, width); + } + } + + [HasDefaultValue(1, false)] + public int RuinCount + { + get { return ruinCount; } + set { ruinCount = MathHelper.Clamp(value, 0, 10); } + } + + [HasDefaultValue(0.4f, false)] + public float BottomHoleProbability + { + get { return bottomHoleProbability; } + set { bottomHoleProbability = MathHelper.Clamp(value, 0.0f, 1.0f); } + } + + //public LevelGenerationParams() + //{ + // Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); + + // width = 100000.0f; + // height = 50000.0f; + + // voronoiSiteInterval = 2000.0f; + // voronoiSiteVariance = new Vector2(voronoiSiteInterval, voronoiSiteInterval) * 0.4f; + + // mainPathNodeIntervalRange = new Vector2(5000.0f, 10000.0f); + + // float brightness = Rand.Range(1.0f, 1.3f, false); + // BackgroundColor = Color.Lerp(new Color(11, 18, 26), new Color(50, 46, 20), Rand.Range(0.0f, 1.0f, false)) * brightness; + // BackgroundColor = new Color(BackgroundColor, 1.0f); + + // smallTunnelCount = 5; + // smallTunnelLengthRange = new Vector2(5000.0f, 10000.0f); + + // ruinCount = 1; + + // bottomHoleProbability = Rand.Range(0.1f, 0.8f, false); + + // BackgroundSpriteAmount = (int)((new Vector2(width, height)).Length() / 100); + //} + + public static LevelGenerationParams GetRandom(string seed) + { + Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); + + if (presets == null || !presets.Any()) + { + DebugConsole.ThrowError("Level generation presets not found - using default presets"); + return new LevelGenerationParams(null); + } + + return presets[Rand.Range(0, presets.Count, false)]; + } + + private LevelGenerationParams(XElement element) + { + Name = element==null ? "default" : element.Name.ToString(); + ObjectProperties = ObjectProperty.InitProperties(this, element); + + Vector3 colorVector = ToolBox.GetAttributeVector3(element, "BackgroundColor", new Vector3(50, 46, 20)); + BackgroundColor = new Color((int)colorVector.X, (int)colorVector.Y, (int)colorVector.Z); + + VoronoiSiteInterval = ToolBox.GetAttributeVector2(element, "VoronoiSiteInterval", new Vector2(3000, 3000)); + + VoronoiSiteVariance = ToolBox.GetAttributeVector2(element, "VoronoiSiteVariance", new Vector2(voronoiSiteInterval.X, voronoiSiteInterval.Y) * 0.4f); + + MainPathNodeIntervalRange = ToolBox.GetAttributeVector2(element, "MainPathNodeIntervalRange", new Vector2(5000.0f, 10000.0f)); + + SmallTunnelLengthRange = ToolBox.GetAttributeVector2(element, "SmallTunnelLengthRange", new Vector2(5000.0f, 10000.0f)); + } + + public static void LoadPresets() + { + presets = new List(); + + var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.LevelGenerationPresets); + if (!files.Any()) + { + files.Add("Content/Map/LevelGenerationParameters.xml"); + } + + foreach (string file in files) + { + + XDocument doc = ToolBox.TryLoadXml(file); + if (doc == null || doc.Root == null) return; + + foreach (XElement element in doc.Root.Elements()) + { + presets.Add(new LevelGenerationParams(element)); + } + } + } + } +} diff --git a/Subsurface/Source/Map/Levels/LevelRenderer.cs b/Subsurface/Source/Map/Levels/LevelRenderer.cs index c2f64a0b3..2eb4e5c96 100644 --- a/Subsurface/Source/Map/Levels/LevelRenderer.cs +++ b/Subsurface/Source/Map/Levels/LevelRenderer.cs @@ -68,7 +68,9 @@ namespace Barotrauma public void Update(float deltaTime) { - dustOffset -= Vector2.UnitY * 10.0f * (float)deltaTime; + dustOffset -= Vector2.UnitY * 10.0f * deltaTime; + + backgroundSpriteManager.Update(deltaTime); } public void SetWallVertices(VertexPositionTexture[] vertices) @@ -196,7 +198,6 @@ namespace Barotrauma public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam) { - if (wallVertices == null) return; basicEffect.World = cam.ShaderTransform diff --git a/Subsurface/Source/Map/Levels/VoronoiElements.cs b/Subsurface/Source/Map/Levels/VoronoiElements.cs index 11531e788..6755c57f8 100644 --- a/Subsurface/Source/Map/Levels/VoronoiElements.cs +++ b/Subsurface/Source/Map/Levels/VoronoiElements.cs @@ -201,6 +201,8 @@ namespace Voronoi2 public bool isSolid; + public bool OutsideLevel; + public Vector2 Center { get { return (point1 + point2) / 2.0f; } @@ -216,10 +218,25 @@ namespace Voronoi2 { return cell1; } - else + + return null; + } + + /// + /// Returns the normal of the edge that points outwards from the specified cell + /// + public Vector2 GetNormal(VoronoiCell cell) + { + Vector2 dir = Vector2.Normalize(point1 - point2); + + Vector2 normal = new Vector2(dir.Y, -dir.X); + + if (cell != null && Vector2.Dot(normal, Vector2.Normalize(Center - cell.Center)) < 0) { - return null; + normal = -normal; } + + return normal; } } diff --git a/Subsurface/Source/Map/Levels/WrappingWall.cs b/Subsurface/Source/Map/Levels/WrappingWall.cs index cd290a1e5..1c200fa6a 100644 --- a/Subsurface/Source/Map/Levels/WrappingWall.cs +++ b/Subsurface/Source/Map/Levels/WrappingWall.cs @@ -48,28 +48,16 @@ namespace Barotrauma get { return midPos; } } - public WrappingWall(List pathCells, List mapCells, float maxY, int dir = -1) + public WrappingWall(List pathCells, List mapCells, Rectangle ignoredArea, int dir = -1) { cells = new List(); - VoronoiCell lowestPathCell = null; - foreach (VoronoiCell pathCell in pathCells) - { - if (lowestPathCell == null || pathCell.Center.Y < lowestPathCell.Center.Y) - { - lowestPathCell = pathCell; - } - } - - float bottomY = Math.Max(lowestPathCell.Center.Y, maxY); - VoronoiCell edgeCell = null; foreach (VoronoiCell cell in mapCells) { - if (cell.Center.Y > bottomY) continue; - if (edgeCell == null - || (dir < 0 && cell.Center.X < edgeCell.Center.X) - || (dir > 0 && cell.Center.X > edgeCell.Center.X)) + 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; } diff --git a/Subsurface/Source/Utils/ToolBox.cs b/Subsurface/Source/Utils/ToolBox.cs index 4dd685317..31e0d166b 100644 --- a/Subsurface/Source/Utils/ToolBox.cs +++ b/Subsurface/Source/Utils/ToolBox.cs @@ -61,8 +61,8 @@ namespace Barotrauma } public static object GetAttributeObject(XElement element, string name) - { - if (element.Attribute(name) == null) return null; + { + if (element == null || element.Attribute(name) == null) return null; return GetAttributeObject(element.Attribute(name)); } @@ -106,7 +106,7 @@ namespace Barotrauma public static string GetAttributeString(XElement element, string name, string defaultValue) { - if (element.Attribute(name) == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; return GetAttributeString(element.Attribute(name), defaultValue); } @@ -119,7 +119,7 @@ namespace Barotrauma public static float GetAttributeFloat(XElement element, string name, float defaultValue) { - if (element.Attribute(name) == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; float val = defaultValue; @@ -158,7 +158,7 @@ namespace Barotrauma public static int GetAttributeInt(XElement element, string name, int defaultValue) { - if (element.Attribute(name) == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; int val = defaultValue; @@ -176,10 +176,9 @@ namespace Barotrauma public static bool GetAttributeBool(XElement element, string name, bool defaultValue) { - var attribute = element.Attribute(name); - if (attribute == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; - return GetAttributeBool(attribute, defaultValue); + return GetAttributeBool(element.Attribute(name), defaultValue); } public static bool GetAttributeBool(XAttribute attribute, bool defaultValue) @@ -206,16 +205,25 @@ namespace Barotrauma public static Vector2 GetAttributeVector2(XElement element, string name, Vector2 defaultValue) { - if (element.Attribute(name) == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; string val = element.Attribute(name).Value; return ParseToVector2(val); } + + public static Vector3 GetAttributeVector3(XElement element, string name, Vector3 defaultValue) + { + if (element == null || element.Attribute(name) == null) return defaultValue; + + string val = element.Attribute(name).Value; + + return ParseToVector3(val); + } public static Vector4 GetAttributeVector4(XElement element, string name, Vector4 defaultValue) { - if (element.Attribute(name) == null) return defaultValue; + if (element == null || element.Attribute(name) == null) return defaultValue; string val = element.Attribute(name).Value; @@ -259,6 +267,26 @@ namespace Barotrauma return vector.X.ToString("G", CultureInfo.InvariantCulture) + "," + vector.Y.ToString("G", CultureInfo.InvariantCulture); } + public static Vector3 ParseToVector3(string stringVector3, bool errorMessages = true) + { + string[] components = stringVector3.Split(','); + + Vector3 vector = Vector3.Zero; + + if (components.Length!=3) + { + if (!errorMessages) return vector; + DebugConsole.ThrowError("Failed to parse the string ''"+stringVector3+"'' to Vector3"); + return vector; + } + + float.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.X); + float.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Y); + float.TryParse(components[2], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Z); + + return vector; + } + public static Vector4 ParseToVector4(string stringVector4, bool errorMessages = true) { string[] components = stringVector4.Split(',');