diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index a241cdf17..62de4f09e 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -134,6 +134,10 @@ + + + + @@ -357,6 +361,13 @@ Designer PreserveNewest + + Designer + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -446,11 +457,15 @@ PreserveNewest + + PreserveNewest + PreserveNewest PreserveNewest + Designer PreserveNewest @@ -706,6 +721,16 @@ PreserveNewest + + Designer + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -974,6 +999,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -1166,6 +1197,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Subsurface/Content/Characters/Fractalguardian/fractalguardian.png b/Subsurface/Content/Characters/Fractalguardian/fractalguardian.png new file mode 100644 index 000000000..3f5fec6e5 Binary files /dev/null and b/Subsurface/Content/Characters/Fractalguardian/fractalguardian.png differ diff --git a/Subsurface/Content/Characters/Fractalguardian/fractalguardian.xml b/Subsurface/Content/Characters/Fractalguardian/fractalguardian.xml new file mode 100644 index 000000000..41762ff4f --- /dev/null +++ b/Subsurface/Content/Characters/Fractalguardian/fractalguardian.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/Characters/Fractalguardian/guardian1.ogg b/Subsurface/Content/Characters/Fractalguardian/guardian1.ogg new file mode 100644 index 000000000..780c2f806 Binary files /dev/null and b/Subsurface/Content/Characters/Fractalguardian/guardian1.ogg differ diff --git a/Subsurface/Content/Characters/Fractalguardian/guardian2.ogg b/Subsurface/Content/Characters/Fractalguardian/guardian2.ogg new file mode 100644 index 000000000..bad6f3228 Binary files /dev/null and b/Subsurface/Content/Characters/Fractalguardian/guardian2.ogg differ diff --git a/Subsurface/Content/Items/Artifacts/alientools.png b/Subsurface/Content/Items/Artifacts/alientools.png new file mode 100644 index 000000000..d9a05248b Binary files /dev/null and b/Subsurface/Content/Items/Artifacts/alientools.png differ diff --git a/Subsurface/Content/Items/Artifacts/artifact.png b/Subsurface/Content/Items/Artifacts/artifact.png index 6b969ebc1..e53070be7 100644 Binary files a/Subsurface/Content/Items/Artifacts/artifact.png and b/Subsurface/Content/Items/Artifacts/artifact.png differ diff --git a/Subsurface/Content/Items/Artifacts/artifacts.xml b/Subsurface/Content/Items/Artifacts/artifacts.xml index c3f83105d..07df4083e 100644 --- a/Subsurface/Content/Items/Artifacts/artifacts.xml +++ b/Subsurface/Content/Items/Artifacts/artifacts.xml @@ -4,7 +4,7 @@ name="Skyholder Artifact" pickdistance="150"> - + @@ -12,9 +12,8 @@ - - - + + @@ -25,7 +24,7 @@ name="Thermal Artifact" pickdistance="150"> - + @@ -34,7 +33,7 @@ - + @@ -47,7 +46,7 @@ name="Faraday Artifact" pickdistance="150"> - + @@ -56,8 +55,87 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Items/Diving/divinggear.xml b/Subsurface/Content/Items/Diving/divinggear.xml index dbd6e5b2d..b3c8354ea 100644 --- a/Subsurface/Content/Items/Diving/divinggear.xml +++ b/Subsurface/Content/Items/Diving/divinggear.xml @@ -47,11 +47,15 @@ + + + + @@ -92,12 +96,16 @@ + + + + diff --git a/Subsurface/Content/Items/Medical/medical.xml b/Subsurface/Content/Items/Medical/medical.xml index ae81ef990..aacf20bdf 100644 --- a/Subsurface/Content/Items/Medical/medical.xml +++ b/Subsurface/Content/Items/Medical/medical.xml @@ -27,33 +27,6 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Map/StructurePrefabs.xml b/Subsurface/Content/Map/StructurePrefabs.xml index df604cedb..31af55b35 100644 --- a/Subsurface/Content/Map/StructurePrefabs.xml +++ b/Subsurface/Content/Map/StructurePrefabs.xml @@ -109,4 +109,32 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Map/ruins.png b/Subsurface/Content/Map/ruins.png new file mode 100644 index 000000000..08f38ca14 Binary files /dev/null and b/Subsurface/Content/Map/ruins.png differ diff --git a/Subsurface/Content/Map/ruins2.png b/Subsurface/Content/Map/ruins2.png new file mode 100644 index 000000000..2699a689b Binary files /dev/null and b/Subsurface/Content/Map/ruins2.png differ diff --git a/Subsurface/Content/Missions.xml b/Subsurface/Content/Missions.xml index 6ab71adfc..7f7f16426 100644 --- a/Subsurface/Content/Missions.xml +++ b/Subsurface/Content/Missions.xml @@ -8,6 +8,7 @@ radarlabel="Infrasonic signal" failuremessage="Retrieving the artifact failed" successmessage="The artifact has been succesfully retrieved" + spawntype="ruin" itemname="Skyholder Artifact"> @@ -20,6 +21,7 @@ radarlabel="Infrasonic signal" failuremessage="Retrieving the artifact failed" successmessage="The artifact has been succesfully retrieved" + spawntype="ruin" itemname="Thermal Artifact"> diff --git a/Subsurface/Content/Particles/ParticlePrefabs.xml b/Subsurface/Content/Particles/ParticlePrefabs.xml index 83cce1985..126df77ef 100644 --- a/Subsurface/Content/Particles/ParticlePrefabs.xml +++ b/Subsurface/Content/Particles/ParticlePrefabs.xml @@ -193,6 +193,18 @@ + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/randomevents.xml b/Subsurface/Content/randomevents.xml index b6fee3e35..13a9857e5 100644 --- a/Subsurface/Content/randomevents.xml +++ b/Subsurface/Content/randomevents.xml @@ -5,6 +5,7 @@ commonness="10" difficulty="10" minamount="2" maxamount="3" + spawntype="mainpath,cave,ruin" musictype="monster"/> + + ip.Name == itemName) as ItemPrefab; + + string spawnPositionTypeStr = ToolBox.GetAttributeString(element, "spawntype", ""); + + if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) || + !Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType)) + { + spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin; + } if (itemPrefab == null) { @@ -39,7 +49,7 @@ namespace Barotrauma public override void Start(Level level) { - Vector2 position = Level.Loaded.GetRandomItemPos(30.0f); + Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 30.0f); item = new Item(itemPrefab, position, null); item.MoveWithLevel = true; diff --git a/Subsurface/Source/Events/MonsterEvent.cs b/Subsurface/Source/Events/MonsterEvent.cs index aa9122064..1bd0e6eaa 100644 --- a/Subsurface/Source/Events/MonsterEvent.cs +++ b/Subsurface/Source/Events/MonsterEvent.cs @@ -14,6 +14,8 @@ namespace Barotrauma private bool spawnDeep; + private Level.PositionType spawnPosType; + public override string ToString() { return "ScriptedEvent (" + characterFile + ")"; @@ -27,6 +29,14 @@ namespace Barotrauma minAmount = ToolBox.GetAttributeInt(element, "minamount", 1); maxAmount = Math.Max(ToolBox.GetAttributeInt(element, "maxamount", 1), minAmount); + var spawnPosTypeStr = ToolBox.GetAttributeString(element, "spawntype", ""); + + if (string.IsNullOrWhiteSpace(spawnPosTypeStr) || + !Enum.TryParse(spawnPosTypeStr, true, out spawnPosType)) + { + spawnPosType = Level.PositionType.MainPath; + } + spawnDeep = ToolBox.GetAttributeBool(element, "spawndeep", false); } @@ -37,7 +47,9 @@ namespace Barotrauma private void SpawnMonsters() { - WayPoint randomWayPoint = WayPoint.GetRandom(SpawnType.Enemy); + //WayPoint randomWayPoint = WayPoint.GetRandom(SpawnType.Enemy); + + Vector2 spawnPos = Level.Loaded.GetRandomInterestingPosition(true, spawnPosType); int amount = Rand.Range(minAmount, maxAmount, false); @@ -45,16 +57,14 @@ namespace Barotrauma for (int i = 0; i < amount; i++) { - Vector2 position = (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.Position; - if (spawnDeep) { - position.Y -= Level.Loaded.Size.Y; + spawnPos.Y -= Level.Loaded.Size.Y; } - position.X += Rand.Range(-0.5f, 0.5f, false); - position.Y += Rand.Range(-0.5f, 0.5f, false); - monsters[i] = Character.Create(characterFile, position, null, GameMain.Client != null); + spawnPos.X += Rand.Range(-0.5f, 0.5f, false); + spawnPos.Y += Rand.Range(-0.5f, 0.5f, false); + monsters[i] = Character.Create(characterFile, spawnPos, null, GameMain.Client != null); } } diff --git a/Subsurface/Source/Items/Components/Holdable/Propulsion.cs b/Subsurface/Source/Items/Components/Holdable/Propulsion.cs index 845ecdbf1..f3a6e285d 100644 --- a/Subsurface/Source/Items/Components/Holdable/Propulsion.cs +++ b/Subsurface/Source/Items/Components/Holdable/Propulsion.cs @@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components public Propulsion(Item item, XElement element) : base(item,element) { - switch (ToolBox.GetAttributeString(element, "usablein", "air").ToLowerInvariant()) + switch (ToolBox.GetAttributeString(element, "usablein", "both").ToLowerInvariant()) { case "air": usableIn = ParticlePrefab.DrawTargetType.Air; @@ -38,6 +38,7 @@ namespace Barotrauma.Items.Components case "water": usableIn = ParticlePrefab.DrawTargetType.Water; break; + case "both": default: usableIn = ParticlePrefab.DrawTargetType.Both; break; @@ -49,7 +50,7 @@ namespace Barotrauma.Items.Components if (character == null) return false; if (!character.IsKeyDown(InputType.Aim) || character.Stun>0.0f) return false; - if (item.InWater) + if (character.AnimController.InWater) { if (usableIn == ParticlePrefab.DrawTargetType.Air) return true; } diff --git a/Subsurface/Source/Map/Levels/CaveGenerator.cs b/Subsurface/Source/Map/Levels/CaveGenerator.cs index f24b7ef65..45a34615e 100644 --- a/Subsurface/Source/Map/Levels/CaveGenerator.cs +++ b/Subsurface/Source/Map/Levels/CaveGenerator.cs @@ -108,9 +108,23 @@ namespace Barotrauma allowedNextCells.Add(adjacent); } - - if (allowedNextCells.Count == 0) break; + if (allowedNextCells.Count == 0) + { + if (i>5) break; + + foreach (GraphEdge edge in pathCell.edges) + { + var adjacent = edge.AdjacentCell(pathCell); + if (adjacent == null || + adjacent.CellType == CellType.Removed) continue; + + allowedNextCells.Add(adjacent); + } + + if (allowedNextCells.Count == 0) break; + } + //randomly pick one of the adjacent cells as the next cell pathCell = allowedNextCells[Rand.Int(allowedNextCells.Count, false)]; @@ -388,6 +402,8 @@ namespace Barotrauma bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]); } + if (cell.CellType == CellType.Empty) continue; + triangles = MathUtils.TriangulateConvexHull(bodyPoints, cell.Center); Body edgeBody = new Body(GameMain.World); @@ -403,7 +419,7 @@ namespace Barotrauma Vertices bodyVertices = new Vertices(triangles[i]); FixtureFactory.AttachPolygon(bodyVertices, 5.0f, edgeBody); } - + edgeBody.UserData = cell; edgeBody.SleepingAllowed = false; edgeBody.BodyType = BodyType.Kinematic; @@ -442,7 +458,7 @@ namespace Barotrauma foreach (VoronoiCell cell in cells) { - if (cell.body == null) continue; + //if (cell.body == null) continue; foreach (GraphEdge edge in cell.edges) { if (!edge.isSolid) continue; @@ -482,7 +498,7 @@ namespace Barotrauma #if DEBUG DebugConsole.ThrowError("Invalid right normal"); #endif - GameMain.World.RemoveBody(cell.body); + if (cell.body != null) GameMain.World.RemoveBody(cell.body); cell.body = null; leftNormal = Vector2.UnitX; break; @@ -505,7 +521,7 @@ namespace Barotrauma #if DEBUG DebugConsole.ThrowError("Invalid right normal"); #endif - GameMain.World.RemoveBody(cell.body); + if (cell.body != null) GameMain.World.RemoveBody(cell.body); cell.body = null; rightNormal = Vector2.UnitX; break; diff --git a/Subsurface/Source/Map/Levels/Level.cs b/Subsurface/Source/Map/Levels/Level.cs index 99cb483d4..05396c507 100644 --- a/Subsurface/Source/Map/Levels/Level.cs +++ b/Subsurface/Source/Map/Levels/Level.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Collections.Generic; using System.Diagnostics; using Voronoi2; +using Barotrauma.RuinGeneration; namespace Barotrauma { @@ -20,16 +21,22 @@ namespace Barotrauma { get { return loaded; } } + + [Flags] + public enum PositionType + { + MainPath=1, Cave=2, Ruin=4 + } struct InterestingPosition { public readonly Vector2 Position; - public readonly bool IsLarge; + public readonly PositionType PositionType; - public InterestingPosition(Vector2 position, bool isLarge) + public InterestingPosition(Vector2 position, PositionType positionType) { Position = position; - IsLarge = isLarge; + PositionType = positionType; } } @@ -64,6 +71,8 @@ namespace Barotrauma private List positionsOfInterest; + private List ruins; + private Color backgroundColor; public Vector2 StartPosition @@ -80,6 +89,11 @@ namespace Barotrauma { get { return endPosition; } } + + public List Ruins + { + get { return ruins; } + } public WrappingWall[,] WrappingWalls { @@ -116,8 +130,6 @@ namespace Barotrauma this.Difficulty = difficulty; - positionsOfInterest = new List(); - borders = new Rectangle(0, 0, width, height); } @@ -147,10 +159,11 @@ namespace Barotrauma Stopwatch sw = new Stopwatch(); sw.Start(); - if (loaded != null) loaded.Unload(); - + if (loaded != null) loaded.Unload(); loaded = this; + positionsOfInterest = new List(); + renderer = new LevelRenderer(this); Voronoi voronoi = new Voronoi(1.0); @@ -170,8 +183,8 @@ namespace Barotrauma float minWidth = Submarine.Loaded == null ? 0.0f : Math.Max(Submarine.Borders.Width, Submarine.Borders.Height); minWidth = Math.Max(minWidth, 6500.0f); - startPosition = new Vector2((int)minWidth * 2, Rand.Range((int)minWidth * 2, borders.Height - (int)minWidth * 2, false)); - endPosition = new Vector2(borders.Width - (int)minWidth * 2, Rand.Range((int)minWidth * 2, borders.Height - (int)minWidth * 2, false)); + 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)); List pathNodes = new List(); Rectangle pathBorders = borders;// new Rectangle((int)minWidth, (int)minWidth, borders.Width - (int)minWidth * 2, borders.Height - (int)minWidth); @@ -189,7 +202,7 @@ namespace Barotrauma for (int i = 2; i < pathNodes.Count; i+=3 ) { - positionsOfInterest.Add(new InterestingPosition(pathNodes[i], true)); + positionsOfInterest.Add(new InterestingPosition(pathNodes[i], PositionType.MainPath)); } pathNodes.Add(endPosition); @@ -258,9 +271,11 @@ namespace Barotrauma Debug.WriteLine("find cells: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); - List pathCells = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize, + List mainPath = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize, new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.3f, mirror); + List pathCells = new List(mainPath); + EnlargeMainPath(pathCells, minWidth); foreach (InterestingPosition positionOfInterest in positionsOfInterest) @@ -284,9 +299,9 @@ namespace Barotrauma var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders); - positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), false)); + positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), PositionType.Cave)); - if (tunnel.Count() > 4) positionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count() / 2], false)); + if (tunnel.Count() > 4) positionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count() / 2], PositionType.Cave)); pathCells.AddRange(newPathCells); } @@ -307,7 +322,7 @@ namespace Barotrauma } //generate some narrow caves - int caveAmount = Rand.Int(3, false); + int caveAmount = 0;// Rand.Int(3, false); List usedCaveCells = new List(); for (int i = 0; i < caveAmount; i++) { @@ -367,7 +382,7 @@ namespace Barotrauma for (int j = cavePathCells.Count / 2; j < cavePathCells.Count; j += 10) { - positionsOfInterest.Add(new InterestingPosition(cavePathCells[j].Center, false)); + positionsOfInterest.Add(new InterestingPosition(cavePathCells[j].Center, PositionType.Cave)); } } @@ -389,11 +404,86 @@ 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)) + { + 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)); + } + 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(cells, out bodyVertices); + bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out bodyVertices); renderer.SetBodyVertices(bodyVertices.ToArray()); renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells)); @@ -465,6 +555,10 @@ namespace Barotrauma endPosition = temp; } + + + //RuinGeneration.RuinGenerator.Draw(spriteBatch); + Debug.WriteLine("**********************************************************************************"); Debug.WriteLine("Generated a map with " + sites.Count + " sites in " + sw.ElapsedMilliseconds + " ms"); @@ -572,40 +666,7 @@ namespace Barotrauma minDistance *= 0.5f; do { - var closeCells = GetCells(position, 1); - - foreach (VoronoiCell cell in closeCells) - { - bool tooClose = false; - foreach (GraphEdge edge in cell.edges) - { - if (Math.Abs(position.X - edge.point1.X) < minDistance || - Math.Abs(position.Y - edge.point1.Y) < minDistance || - Math.Abs(position.X - edge.point2.X) < minDistance || - Math.Abs(position.Y - edge.point2.Y) < minDistance) - { - tooClose = true; - break; - } - } - - 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]); - } - } - } + tooCloseCells.AddRange(GetTooCloseCells(position, minDistance)); position += Vector2.Normalize(emptyCells[targetCellIndex].Center - position) * step; @@ -616,6 +677,47 @@ namespace Barotrauma return tooCloseCells; } + private List GetTooCloseCells(Vector2 position, float minDistance) + { + List tooCloseCells = new List(); + + 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); + } + + //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; + } + /// /// remove all cells except those that are adjacent to the empty cells @@ -636,7 +738,7 @@ namespace Barotrauma return newCells; } - public Vector2 GetRandomItemPos(float offsetFromWall = 10.0f) + public Vector2 GetRandomItemPos(PositionType spawnPosType, float offsetFromWall = 10.0f) { if (!positionsOfInterest.Any()) return Size*0.5f; @@ -647,16 +749,16 @@ namespace Barotrauma int tries = 0; do { - Vector2 startPos = ConvertUnits.ToSimUnits(Level.Loaded.GetRandomInterestingPosition(true, false)); - - Vector2 endPos = startPos - ConvertUnits.ToSimUnits(Vector2.UnitY * Size.Y); + Vector2 startPos = Level.Loaded.GetRandomInterestingPosition(true, spawnPosType); + + Vector2 endPos = startPos - Vector2.UnitY * Size.Y; if (Submarine.PickBody( - startPos, - endPos, + ConvertUnits.ToSimUnits(startPos), + ConvertUnits.ToSimUnits(endPos), null, Physics.CollisionLevel) != null) { - position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition + Vector2.Normalize(startPos - endPos)*offsetFromWall); + position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.Normalize(startPos - endPos)*offsetFromWall; break; } @@ -672,18 +774,17 @@ namespace Barotrauma return position; } - public Vector2 GetRandomInterestingPosition(bool useSyncedRand, bool? preferLarge) + public Vector2 GetRandomInterestingPosition(bool useSyncedRand, PositionType positionType) { if (!positionsOfInterest.Any()) return Size * 0.5f; - if (preferLarge==null) + var matchingPositions = positionsOfInterest.FindAll(p => positionType.HasFlag(p.PositionType)); + if (!matchingPositions.Any()) + { return positionsOfInterest[Rand.Int(positionsOfInterest.Count, !useSyncedRand)].Position; + } - - var positionsWithSpace = positionsOfInterest.FindAll(p => (bool)preferLarge == p.IsLarge); - if (!positionsWithSpace.Any()) return Size * 0.5f; - - return positionsWithSpace[Rand.Int(positionsWithSpace.Count, !useSyncedRand)].Position; + return matchingPositions[Rand.Int(matchingPositions.Count, !useSyncedRand)].Position; } public void Update (float deltaTime) @@ -705,7 +806,18 @@ namespace Barotrauma { foreach (InterestingPosition pos in positionsOfInterest) { - GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X-15.0f, -pos.Position.Y-15.0f), new Vector2(30.0f, 30.0f), Color.Gold, true); + 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); } } } @@ -764,7 +876,7 @@ namespace Barotrauma private void Unload() { - renderer.Dispose(); + if (renderer!=null) renderer.Dispose(); renderer = null; for (int side = 0; side < 2; side++) diff --git a/Subsurface/Source/Map/Levels/Ruins/BTRoom.cs b/Subsurface/Source/Map/Levels/Ruins/BTRoom.cs new file mode 100644 index 000000000..dd0eab0de --- /dev/null +++ b/Subsurface/Source/Map/Levels/Ruins/BTRoom.cs @@ -0,0 +1,160 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.RuinGeneration +{ + /// + /// nodes of a binary tree used for generating underwater "dungeons" + /// + class BTRoom : RuinShape + { + private BTRoom[] subRooms; + + public BTRoom Parent + { + get; + private set; + } + + public Corridor Corridor + { + get; + set; + } + + public BTRoom[] SubRooms + { + get { return subRooms; } + } + + public BTRoom Adjacent + { + get; + private set; + } + + public BTRoom(Rectangle rect) + { + this.rect = rect; + } + + public void Split(float minDivRatio, float verticalProbability = 0.5f) + { + subRooms = new BTRoom[2]; + + if (Rand.Range(0.0f, 1.0f, false) < verticalProbability) + { + SplitVertical(minDivRatio); + } + else + { + SplitHorizontal(minDivRatio); + } + + subRooms[0].Parent = this; + subRooms[1].Parent = this; + + subRooms[0].Adjacent = subRooms[1]; + subRooms[1].Adjacent = subRooms[0]; + } + + private void SplitHorizontal(float minDivRatio) + { + float div = Rand.Range(minDivRatio, 1.0f - minDivRatio, false); + subRooms[0] = new BTRoom(new Rectangle(rect.X, rect.Y, rect.Width, (int)(rect.Height * div))); + subRooms[1] = new BTRoom(new Rectangle(rect.X, rect.Y + subRooms[0].rect.Height, rect.Width, rect.Height - subRooms[0].rect.Height)); + + } + + private void SplitVertical(float minDivRatio) + { + float div = Rand.Range(minDivRatio, 1.0f - minDivRatio, false); + subRooms[0] = new BTRoom(new Rectangle(rect.X, rect.Y, (int)(rect.Width * div), rect.Height)); + subRooms[1] = new BTRoom(new Rectangle(rect.X + subRooms[0].rect.Width, rect.Y, rect.Width - subRooms[0].rect.Width, rect.Height)); + } + + public override void CreateWalls() + { + Walls = new List(); + + Walls.Add(new Line(new Vector2(Rect.X, Rect.Y), new Vector2(Rect.Right, Rect.Y), RuinStructureType.Wall)); + Walls.Add(new Line(new Vector2(Rect.X, Rect.Bottom), new Vector2(Rect.Right, Rect.Bottom), RuinStructureType.Wall)); + + Walls.Add(new Line(new Vector2(Rect.X, Rect.Y), new Vector2(Rect.X, Rect.Bottom), RuinStructureType.Wall)); + Walls.Add(new Line(new Vector2(Rect.Right, Rect.Y), new Vector2(Rect.Right, Rect.Bottom), RuinStructureType.Wall)); + } + + public void Scale(Vector2 scale) + { + rect.Inflate((scale.X - 1.0f) * 0.5f * rect.Width, (scale.Y - 1.0f) * 0.5f * rect.Height); + } + + public List GetLeaves() + { + return GetLeaves(new List()); + } + + private List GetLeaves(List leaves) + { + if (subRooms == null) + { + leaves.Add(this); + } + else + { + subRooms[0].GetLeaves(leaves); + subRooms[1].GetLeaves(leaves); + } + + return leaves; + } + + public void GenerateCorridors(int minWidth, int maxWidth, List corridors) + { + if (Adjacent != null && Corridor == null) + { + Corridor = new Corridor(this, Rand.Range(minWidth, maxWidth, false), corridors); + } + + if (subRooms != null) + { + subRooms[0].GenerateCorridors(minWidth, maxWidth, corridors); + subRooms[1].GenerateCorridors(minWidth, maxWidth, corridors); + } + } + + public static void CalculateDistancesFromEntrance(BTRoom entrance, List corridors) + { + entrance.CalculateDistanceFromEntrance(1, new List(corridors)); + } + + private void CalculateDistanceFromEntrance(int currentDist, List corridors) + { + if (DistanceFromEntrance == 0) + { + DistanceFromEntrance = currentDist; + } + else + { + DistanceFromEntrance = Math.Min(currentDist, DistanceFromEntrance); + } + + currentDist++; + + for (int i = corridors.Count - 1; i >= 0; i = Math.Min(i - 1, corridors.Count - 1)) + { + var corridor = corridors[i]; + + if (!corridor.ConnectedRooms.Contains(this)) continue; + + corridors.RemoveAt(i); + + corridor.ConnectedRooms[corridor.ConnectedRooms[0] == this ? 1 : 0].CalculateDistanceFromEntrance(currentDist, corridors); + } + } + } +} diff --git a/Subsurface/Source/Map/Levels/Ruins/Corridor.cs b/Subsurface/Source/Map/Levels/Ruins/Corridor.cs new file mode 100644 index 000000000..042ffceca --- /dev/null +++ b/Subsurface/Source/Map/Levels/Ruins/Corridor.cs @@ -0,0 +1,189 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.RuinGeneration +{ + + class Corridor : RuinShape + { + private bool isHorizontal; + + public Rectangle Rect + { + get { return rect; } + } + + public bool IsHorizontal + { + get { return isHorizontal; } + } + + public BTRoom[] ConnectedRooms + { + get; + private set; + } + + public Corridor(Rectangle rect) + { + this.rect = rect; + + isHorizontal = rect.Width > rect.Height; + } + + public Corridor(BTRoom room, int width, List corridors) + { + System.Diagnostics.Debug.Assert(room.Adjacent != null); + + ConnectedRooms = new BTRoom[2]; + ConnectedRooms[0] = room; + ConnectedRooms[1] = room.Adjacent; + + Rectangle room1, room2; + + room1 = room.Rect; + room2 = room.Adjacent.Rect; + + isHorizontal = (room1.Right <= room2.X || room2.Right <= room1.X); + + //use the leaves as starting points for the corridor + if (room.SubRooms != null) + { + var leaves1 = room.GetLeaves(); + var leaves2 = room.Adjacent.GetLeaves(); + + var suitableLeaves = GetSuitableLeafRooms(leaves1, leaves2, width, isHorizontal); + room1 = suitableLeaves[0].Rect; + room2 = suitableLeaves[1].Rect; + + ConnectedRooms[0] = suitableLeaves[0]; + ConnectedRooms[1] = suitableLeaves[1]; + } + + if (isHorizontal) + { + int left = Math.Min(room1.Right, room2.Right); + int right = Math.Max(room1.X, room2.X); + + int top = Math.Max(room1.Y, room2.Y); + int bottom = Math.Min(room1.Bottom, room2.Bottom); + + int yPos = Rand.Range(top, bottom - width, false); + + rect = new Rectangle(left, yPos, right - left, width); + } + else if (room1.Y > room2.Bottom || room2.Y > room1.Bottom) + { + int left = Math.Max(room1.X, room2.X); + int right = Math.Min(room1.Right, room2.Right); + + int top = Math.Min(room1.Bottom, room2.Bottom); + int bottom = Math.Max(room1.Y, room2.Y); + + int xPos = Rand.Range(left, right - width, false); + + rect = new Rectangle(xPos, top, width, bottom - top); + } + else + { + DebugConsole.ThrowError("wat"); + } + + room.Corridor = this; + room.Adjacent.Corridor = this; + + for (int i = corridors.Count - 1; i >= 0; i--) + { + var corridor = corridors[i]; + + if (corridor.rect.Intersects(this.rect)) + { + if (isHorizontal && corridor.isHorizontal) + { + if (this.rect.Width < corridor.rect.Width) + return; + else + corridors.RemoveAt(i); + } + else if (!isHorizontal && !corridor.isHorizontal) + { + if (this.rect.Height < corridor.rect.Height) + return; + else + corridors.RemoveAt(i); + } + } + } + + corridors.Add(this); + } + + public override void CreateWalls() + { + + + Walls = new List(); + + if (IsHorizontal) + { + Walls.Add(new Line(new Vector2(Rect.X, Rect.Y), new Vector2(Rect.Right, Rect.Y), RuinStructureType.CorridorWall)); + Walls.Add(new Line(new Vector2(Rect.X, Rect.Bottom), new Vector2(Rect.Right, Rect.Bottom), RuinStructureType.CorridorWall)); + } + else + { + Walls.Add(new Line(new Vector2(Rect.X, Rect.Y), new Vector2(Rect.X, Rect.Bottom), RuinStructureType.CorridorWall)); + Walls.Add(new Line(new Vector2(Rect.Right, Rect.Y), new Vector2(Rect.Right, Rect.Bottom), RuinStructureType.CorridorWall)); + } + } + + /// + /// find two rooms which have two face-two-face walls that we can place a corridor in between + /// + /// + private BTRoom[] GetSuitableLeafRooms(List leaves1, List leaves2, int width, bool isHorizontal) + { + int iOffset = Rand.Int(leaves1.Count, false); + int jOffset = Rand.Int(leaves2.Count, false); + + + for (int iCount = 0; iCount < leaves1.Count; iCount++) + { + int i = (iCount + iOffset) % leaves1.Count; + + for (int jCount = 0; jCount < leaves2.Count; jCount++) + { + int j = (jCount + jOffset) % leaves2.Count; + + if (isHorizontal) + { + //if (Math.Min(leaves1[i].Rect.Bottom, leaves2[i].Rect.Bottom) - Math.Max(leaves1[i].Rect.Y, leaves2[j].Rect.Y) < width) continue; + + + if (leaves1[i].Rect.Y > leaves2[j].Rect.Bottom) continue; + if (leaves1[i].Rect.Bottom < leaves2[j].Rect.Y) continue; + } + else + { + //if (Math.Min(leaves1[i].Rect.Right, leaves2[i].Rect.Right) - Math.Max(leaves1[i].Rect.X, leaves2[j].Rect.X) < width) continue; + + + if (leaves1[i].Rect.X > leaves2[j].Rect.Right) continue; + if (leaves1[i].Rect.Right < leaves2[j].Rect.X) continue; + } + + + return new BTRoom[] { leaves1[i], leaves2[j] }; + } + } + + return null; + } + + + } + +} diff --git a/Subsurface/Source/Map/Levels/Ruins/RuinGenerator.cs b/Subsurface/Source/Map/Levels/Ruins/RuinGenerator.cs new file mode 100644 index 000000000..bf0e948ee --- /dev/null +++ b/Subsurface/Source/Map/Levels/Ruins/RuinGenerator.cs @@ -0,0 +1,422 @@ +using FarseerPhysics.Common; +using FarseerPhysics.Common.PolygonManipulation; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voronoi2; + +namespace Barotrauma.RuinGeneration +{ + abstract class RuinShape + { + protected Rectangle rect; + + public Rectangle Rect + { + get { return rect; } + } + + + public int DistanceFromEntrance + { + get; + protected set; + } + + public List Walls; + + public virtual void CreateWalls() { } + + public Alignment GetLineAlignment(Line line) + { + if (line.A.Y == line.B.Y) + { + if (line.A.Y > rect.Center.Y && line.B.Y > rect.Center.Y) + { + return Alignment.Bottom; + } + else if (line.A.Y < rect.Center.Y && line.B.Y < rect.Center.Y) + { + return Alignment.Top; + } + } + else + { + if (line.A.X < rect.Center.X && line.B.X < rect.Center.X) + { + return Alignment.Left; + } + else if (line.A.X > rect.Center.X && line.B.X > rect.Center.X) + { + return Alignment.Right; + } + } + + return Alignment.Center; + } + + /// + /// Goes through a list of line segments and "clips off" all parts of the lines that are inside the rectangle + /// + public void SplitLines(Rectangle rectangle) + { + List newLines = new List(); + + foreach (Line line in Walls) + { + if (line.A.X == line.B.X) //vertical line + { + //line doesn't intersect the rectangle + if (rectangle.X > line.A.X || rectangle.Right < line.A.X || + rectangle.Y > line.B.Y || rectangle.Bottom < line.A.Y) + { + newLines.Add(line); + } + else if (line.A.Y > rectangle.Y && line.B.Y < rectangle.Bottom) + { + continue; + } + //point A is within the rectangle -> cut a portion from the top of the line + else if (line.A.Y >= rectangle.Y && line.A.Y <= rectangle.Bottom) + { + newLines.Add(new Line(new Vector2(line.A.X, rectangle.Bottom), line.B, line.Type)); + } + //point B is within the rectangle -> cut a portion from the bottom of the line + else if (line.B.Y >= rectangle.Y && line.B.Y <= rectangle.Bottom) + { + newLines.Add(new Line(line.A, new Vector2(line.A.X, rectangle.Y), line.Type)); + } + //rect is in between the lines -> split the line into two + else + { + newLines.Add(new Line(line.A, new Vector2(line.A.X, rectangle.Y), line.Type)); + newLines.Add(new Line(new Vector2(line.A.X, rectangle.Bottom), line.B, line.Type)); + } + } + else if (line.A.Y == line.B.Y) //horizontal line + { + //line doesn't intersect the rectangle + if (rectangle.X > line.B.X || rectangle.Right < line.A.X || + rectangle.Y > line.A.Y || rectangle.Bottom < line.A.Y) + { + + newLines.Add(line); + } + else if (line.A.X > rectangle.X && line.B.X < rectangle.Right) + { + continue; + } + //point A is within the rectangle -> cut a portion from the left side of the line + else if (line.A.X >= rectangle.X && line.A.X <= rectangle.Right) + { + newLines.Add(new Line(new Vector2(rectangle.Right, line.A.Y), line.B, line.Type)); + } + //point B is within the rectangle -> cut a portion from the right side of the line + else if (line.B.X >= rectangle.X && line.B.X <= rectangle.Right) + { + newLines.Add(new Line(line.A, new Vector2(rectangle.X, line.A.Y), line.Type)); + } + //rect is in between the lines -> split the line into two + else + { + newLines.Add(new Line(line.A, new Vector2(rectangle.X, line.A.Y), line.Type)); + newLines.Add(new Line(new Vector2(rectangle.Right, line.A.Y), line.B, line.Type)); + } + } + else + { + DebugConsole.ThrowError("Error in StructureGenerator.SplitLines - lines must be axis aligned"); + } + + } + + Walls = newLines; + } + } + + struct Line + { + public readonly Vector2 A, B; + + public readonly RuinStructureType Type; + + public Line(Vector2 a, Vector2 b, RuinStructureType type) + { + Debug.Assert(a.X <= b.X); + Debug.Assert(a.Y <= b.Y); + + A = a; + B = b; + Type = type; + } + } + + class Ruin + { + private List rooms; + private List corridors; + + private List walls; + + private List allShapes; + + public List RuinShapes + { + get { return allShapes; } + } + + public Rectangle Area + { + get; + private set; + } + + public Ruin(VoronoiCell closestPathCell, List caveCells, Rectangle area) + { + Area = area; + + corridors = new List(); + rooms = new List(); + + walls = new List(); + + allShapes = new List(); + + Generate(closestPathCell, caveCells, area); + } + + public void Generate(VoronoiCell closestPathCell, List caveCells, Rectangle area) + { + corridors.Clear(); + rooms.Clear(); + + //area = new Rectangle(area.X, area.Y - area.Height, area.Width, area.Height); + + int iterations = Rand.Range(3, 4, false); + + float verticalProbability = Rand.Range(0.4f, 0.6f, false); + + BTRoom baseRoom = new BTRoom(area); + + rooms = new List { baseRoom }; + + for (int i = 0; i < iterations; i++) + { + rooms.ForEach(l => l.Split(0.3f, verticalProbability)); + + rooms = baseRoom.GetLeaves(); + } + + foreach (BTRoom leaf in rooms) + { + leaf.Scale + ( + new Vector2(Rand.Range(0.5f, 0.9f, false), Rand.Range(0.5f, 0.9f, false)) + ); + } + + baseRoom.GenerateCorridors(200, 256, corridors); + + walls = new List(); + + rooms.ForEach(leaf => + { + leaf.CreateWalls(); + //walls.AddRange(leaf.Walls); + }); + + //--------------------------- + + BTRoom entranceRoom = null; + float shortestDistance = 0.0f; + foreach (BTRoom leaf in rooms) + { + float distance = Vector2.Distance(leaf.Rect.Center.ToVector2(), closestPathCell.Center); + if (entranceRoom == null || distance < shortestDistance) + { + entranceRoom = leaf; + shortestDistance = distance; + } + } + + rooms.Remove(entranceRoom); + + //var startCell = closestPathCell; + + //Rectangle startCellRect = new Rectangle( + // (int)startCell.edges.Min(e => Math.Min(e.point1.X, e.point2.X)), + // (int)startCell.edges.Min(e => Math.Min(e.point1.Y, e.point2.Y)), + // (int)startCell.edges.Max(e => Math.Max(e.point1.X, e.point2.X)), + // (int)startCell.edges.Max(e => Math.Max(e.point1.Y, e.point2.Y))); + + //startCellRect.Width = startCellRect.Width - startCellRect.X; + //startCellRect.Height = startCellRect.Height - startCellRect.Y; + + //int startX = Math.Min(entranceRoom.Rect.Center.X, startCellRect.Center.X); + //int endX = Math.Max(entranceRoom.Rect.Right, startCellRect.Right); + + //int startY = Math.Min(entranceRoom.Rect.Center.Y, startCellRect.Center.Y); + //int endY = Math.Max(entranceRoom.Rect.Bottom, startCellRect.Bottom); + + //if (entranceRoom.Rect.X > startCellRect.X && entranceRoom.Rect.Right < startCellRect.Right) + //{ + // corridors.Add(new Corridor(new Rectangle(entranceRoom.Rect.Center.X, startY, 128, endY - startY))); + //} + //else if (entranceRoom.Rect.Y > startCellRect.Y && entranceRoom.Rect.Bottom < startCellRect.Bottom) + //{ + // corridors.Add(new Corridor(new Rectangle(startX, entranceRoom.Rect.Center.Y, endX - startX, 128))); + //} + //else + //{ + // corridors.Add(new Corridor(new Rectangle(startX, entranceRoom.Rect.Center.Y, endX - startX, 128))); + // corridors.Add(new Corridor(new Rectangle(endX, startY, 128, endY - startY))); + //} + + //--------------------------- + + foreach (BTRoom leaf in rooms) + { + foreach (Corridor corridor in corridors) + { + leaf.SplitLines(corridor.Rect); + } + + walls.AddRange(leaf.Walls); + } + + + foreach (Corridor corridor in corridors) + { + List corridorWalls = new List(); + + corridor.CreateWalls(); + + foreach (BTRoom leaf in rooms) + { + corridor.SplitLines(leaf.Rect); + } + + foreach (Corridor corridor2 in corridors) + { + if (corridor == corridor2) continue; + corridor.SplitLines(corridor2.Rect); + } + + + walls.AddRange(corridor.Walls); + } + + //leaves.Remove(entranceRoom); + + BTRoom.CalculateDistancesFromEntrance(entranceRoom, corridors); + + allShapes = GenerateStructures(caveCells); + } + + private List GenerateStructures(List caveCells) + { + List shapes = new List(rooms); + shapes.AddRange(corridors); + + //MapEntityPrefab hullPrefab = MapEntityPrefab.list.Find(m => m.Name == "Hull"); + + foreach (RuinShape leaf in shapes) + { + foreach (Line wall in leaf.Walls) + { + var structurePrefab = RuinStructure.GetRandom(leaf is BTRoom ? RuinStructureType.Wall : RuinStructureType.CorridorWall, leaf.GetLineAlignment(wall)); + if (structurePrefab == null) continue; + + float radius = (wall.A.X == wall.B.X) ? (structurePrefab.Prefab as StructurePrefab).Size.X * 0.5f : (structurePrefab.Prefab as StructurePrefab).Size.Y * 0.5f; + + Rectangle rect = new Rectangle( + (int)(wall.A.X - radius), + (int)(wall.B.Y + radius), + (int)((wall.B.X - wall.A.X) + radius*2.0f), + (int)((wall.B.Y - wall.A.Y) + radius*2.0f)); + + var structure = new Structure(rect, structurePrefab.Prefab as StructurePrefab, null); + structure.MoveWithLevel = true; + structure.SetCollisionCategory(Physics.CollisionLevel); + } + + + var background = RuinStructure.GetRandom(RuinStructureType.Back, Alignment.Center); + if (background == null) continue; + + Rectangle backgroundRect = new Rectangle(leaf.Rect.X, leaf.Rect.Y + leaf.Rect.Height, leaf.Rect.Width, leaf.Rect.Height); + + new Structure(backgroundRect, (background.Prefab as StructurePrefab), null).MoveWithLevel = true; + + } + + for (int i = 0; i < shapes.Count*2; i++ ) + { + Alignment[] alignments = new Alignment[] { Alignment.Top, Alignment.Bottom, Alignment.Right, Alignment.Left, Alignment.Center }; + + var prop = RuinStructure.GetRandom(RuinStructureType.Prop, alignments[Rand.Int(alignments.Length, false)]); + + Vector2 size = (prop.Prefab is StructurePrefab) ? (prop.Prefab as StructurePrefab).Size : Vector2.Zero; + + var shape = shapes[Rand.Int(shapes.Count, false)]; + + Vector2 position = shape.Rect.Center.ToVector2(); + if (prop.Alignment.HasFlag(Alignment.Top)) + { + position = new Vector2(Rand.Range(shape.Rect.X+size.X, shape.Rect.Right - size.X, false), shape.Rect.Bottom - 64); + } + else if (prop.Alignment.HasFlag(Alignment.Bottom)) + { + position = new Vector2(Rand.Range(shape.Rect.X + size.X, shape.Rect.Right - size.X, false), shape.Rect.Top + 64); + } + else if (prop.Alignment.HasFlag(Alignment.Right)) + { + position = new Vector2(shape.Rect.Right - 64, Rand.Range(shape.Rect.Y + size.X, shape.Rect.Bottom - size.Y, false)); + } + else if (prop.Alignment.HasFlag(Alignment.Left)) + { + position = new Vector2(shape.Rect.X + 64, Rand.Range(shape.Rect.Y + size.X, shape.Rect.Bottom - size.Y, false)); + } + + if (prop.Prefab is ItemPrefab) + { + var item = new Item(prop.Prefab as ItemPrefab, position, null); + item.MoveWithLevel = true; + } + else + { + new Structure(new Rectangle( + (int)(position.X - size.X/2.0f), (int)(position.Y + size.Y/2.0f), + (int)size.X, (int)size.Y), + prop.Prefab as StructurePrefab, null).MoveWithLevel = true; + } + } + + return shapes; + } + + public void Draw(SpriteBatch spriteBatch) + { + //foreach (BTRoom room in leaves) + //{ + // GUI.DrawRectangle(spriteBatch, room.Rect, Color.White); + //} + + //foreach (Corridor corr in corridors) + //{ + // GUI.DrawRectangle(spriteBatch, corr.Rect, Color.Blue); + //} + + foreach (Line line in walls) + { + GUI.DrawLine(spriteBatch, new Vector2(line.A.X, -line.A.Y), new Vector2(line.B.X, -line.B.Y), Color.Red, 0.0f, 10); + } + } + + } +} diff --git a/Subsurface/Source/Map/Levels/Ruins/RuinStructure.cs b/Subsurface/Source/Map/Levels/Ruins/RuinStructure.cs new file mode 100644 index 000000000..361d0c658 --- /dev/null +++ b/Subsurface/Source/Map/Levels/Ruins/RuinStructure.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Barotrauma.RuinGeneration +{ + [Flags] + enum RuinStructureType + { + Wall = 1, CorridorWall = 2, Prop = 4, Back = 8 + } + + class RuinStructure + { + const string ConfigFile = "Content/Map/RuinConfig.xml"; + + private static List list; + + public readonly MapEntityPrefab Prefab; + + public readonly Alignment Alignment; + + public readonly RuinStructureType Type; + + private int commonness; + + private RuinStructure(XElement element) + { + string prefab = ToolBox.GetAttributeString(element, "prefab", "").ToLowerInvariant(); + Prefab = MapEntityPrefab.list.Find(s => s.Name.ToLowerInvariant() == prefab); + + if (Prefab == null) + { + DebugConsole.ThrowError("Loading ruin structure failed - structure prefab ''"+prefab+" not found"); + return; + } + + string alignmentStr = ToolBox.GetAttributeString(element,"alignment","Bottom"); + if (!Enum.TryParse(alignmentStr, true, out Alignment)) + { + DebugConsole.ThrowError("Error in ruin structure ''"+prefab+"'' - "+alignmentStr+" is not a valid alignment"); + } + + + string typeStr = ToolBox.GetAttributeString(element,"type",""); + if (!Enum.TryParse(typeStr,true, out Type)) + { + DebugConsole.ThrowError("Error in ruin structure ''" + prefab + "'' - " + typeStr + " is not a valid type"); + return; + } + + commonness = ToolBox.GetAttributeInt(element, "commonness", 1); + + list.Add(this); + } + + private static void Load() + { + list = new List(); + + XDocument doc = ToolBox.TryLoadXml(ConfigFile); + if (doc == null || doc.Root == null) return; + + foreach (XElement element in doc.Root.Elements()) + { + new RuinStructure(element); + } + } + + public static RuinStructure GetRandom(RuinStructureType type, Alignment alignment) + { + if (list==null) + { + DebugConsole.Log("Loading ruin structures..."); + Load(); + } + + var matchingStructures = list.FindAll(rs => rs.Type.HasFlag(type) && rs.Alignment.HasFlag(alignment)); + + if (!matchingStructures.Any()) return null; + + int totalCommonness = matchingStructures.Sum(m => m.commonness); + + int randomNumber = Rand.Int(totalCommonness + 1, false); + + foreach (RuinStructure ruinStructure in matchingStructures) + { + + if (randomNumber <= ruinStructure.commonness) + { + return ruinStructure; + } + + randomNumber -= ruinStructure.commonness; + } + + return null; + } + } +} diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 3c3ef0c0f..9361dbe41 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -140,7 +140,7 @@ namespace Barotrauma // } //} } - + public Structure(Rectangle rectangle, StructurePrefab sp, Submarine submarine) : base(sp, submarine) { @@ -602,6 +602,15 @@ namespace Barotrauma UpdateSections(); } + public void SetCollisionCategory(Category collisionCategory) + { + if (bodies == null) return; + foreach (Body body in bodies) + { + body.CollisionCategories = collisionCategory; + } + } + private void UpdateSections() { foreach (Body b in bodies) diff --git a/Subsurface/Source/Map/StructurePrefab.cs b/Subsurface/Source/Map/StructurePrefab.cs index 7c75a84a1..e421982ac 100644 --- a/Subsurface/Source/Map/StructurePrefab.cs +++ b/Subsurface/Source/Map/StructurePrefab.cs @@ -47,6 +47,11 @@ namespace Barotrauma { get { return stairDirection; } } + + public Vector2 Size + { + get { return size; } + } public static void LoadAll(List filePaths) { @@ -63,7 +68,7 @@ namespace Barotrauma } } } - + public static StructurePrefab Load(XElement element) { StructurePrefab sp = new StructurePrefab(); diff --git a/Subsurface/Source/Sounds/SoundPlayer.cs b/Subsurface/Source/Sounds/SoundPlayer.cs index 11f9944cd..fd73d1850 100644 --- a/Subsurface/Source/Sounds/SoundPlayer.cs +++ b/Subsurface/Source/Sounds/SoundPlayer.cs @@ -247,7 +247,11 @@ namespace Barotrauma { suitableMusic = musicClips.Where(x => x != null && x.type == OverrideMusicType).ToList(); } - else if (Submarine.Loaded!=null && Submarine.Loaded.AtDamageDepth) + else if (Character.Controlled != null && Level.Loaded != null && Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition))) + { + suitableMusic = musicClips.Where(x => x != null && x.type == "ruins").ToList(); + } + else if (Submarine.Loaded != null && Submarine.Loaded.AtDamageDepth) { suitableMusic = musicClips.Where(x => x != null && x.type == "deep").ToList(); }