]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
DungeonData rework (#37172)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 17 May 2025 17:10:30 +0000 (03:10 +1000)
committerGitHub <noreply@github.com>
Sat, 17 May 2025 17:10:30 +0000 (03:10 +1000)
* DungeonData rework

Back to fields, serializes better, just make new layers dumby.

* wawawewa

* Fix this

* Fixes

* review

* thanks fork

* fix

57 files changed:
Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenAutoCabling.cs with 92% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBiome.cs with 94% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBiomeMarkerLayer.cs with 97% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBoundaryWall.cs with 80% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.CornerClutter.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCornerClutter.cs with 69% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridor.cs with 89% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridorClutter.cs with 82% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridorDecalSkirting.cs with 93% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.DunGenFill.cs [deleted file]
Content.Server/Procedural/DungeonJob/DungeonJob.DunGenPrefab.cs
Content.Server/Procedural/DungeonJob/DungeonJob.DunGenReplaceTile.cs
Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenDungeonEntrance.cs with 88% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenEntranceFlank.cs with 66% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.DunGenExterior.cs with 88% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenExternalWindow.cs with 86% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs [new file with mode: 0644]
Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs with 98% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenInternalWindow.cs with 84% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenJunction.cs with 87% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenMiddleConnection.cs with 81% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.MobDunGen.cs with 90% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Noise.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoise.cs with 100% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Ore.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.OreDunGen.cs with 100% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.PostGenDungeonConnector.cs [deleted file]
Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenRoomEntrance.cs with 59% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenSplineDungeonConnector.cs with 85% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenWallMount.cs with 52% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.Worm.cs [moved from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenWorm.cs with 93% similarity]
Content.Server/Procedural/DungeonJob/DungeonJob.cs
Content.Shared/EntityTable/EntityTableSystem.cs
Content.Shared/Procedural/DungeonConfig.cs
Content.Shared/Procedural/DungeonData.cs [deleted file]
Content.Shared/Procedural/DungeonGenerators/PrefabDunGen.cs
Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs
Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs [moved from Content.Shared/Procedural/DungeonGenerators/FillGridDunGen.cs with 79% similarity]
Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs
Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs
Content.Shared/Procedural/PostGeneration/AutoCablingDunGen.cs
Content.Shared/Procedural/PostGeneration/BoundaryWallDunGen.cs
Content.Shared/Procedural/PostGeneration/CornerClutterDunGen.cs
Content.Shared/Procedural/PostGeneration/CorridorClutterDunGen.cs
Content.Shared/Procedural/PostGeneration/CorridorDecalSkirtingDunGen.cs
Content.Shared/Procedural/PostGeneration/CorridorDunGen.cs
Content.Shared/Procedural/PostGeneration/DungeonEntranceDunGen.cs
Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs
Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs
Content.Shared/Procedural/PostGeneration/InternalWindowDunGen.cs
Content.Shared/Procedural/PostGeneration/JunctionDunGen.cs
Content.Shared/Procedural/PostGeneration/MiddleConnectionDunGen.cs
Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs
Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs
Content.Shared/Procedural/PostGeneration/WallMountDunGen.cs
Content.Shared/Procedural/PostGeneration/WormCorridorDunGen.cs
Content.Shared/Salvage/SharedSalvageSystem.Magnet.cs
Content.Shared/Storage/EntitySpawnEntry.cs
Resources/Prototypes/Procedural/dungeon_configs.yml
Resources/Prototypes/Procedural/vgroid.yml

similarity index 92%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenAutoCabling.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs
index 1ff28719fc13a704c7587c9276b7103b79897bff..8c49a7d606a17a794863586a1b5ce5001161e550 100644 (file)
@@ -13,14 +13,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="AutoCablingDunGen"/>
     /// </summary>
-    private async Task PostGen(AutoCablingDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(AutoCablingDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Entities.TryGetValue(DungeonDataKey.Cabling, out var ent))
-        {
-            LogDataError(typeof(AutoCablingDunGen));
-            return;
-        }
-
         // There's a lot of ways you could do this.
         // For now we'll just connect every LV cable in the dungeon.
         var cableTiles = new HashSet<Vector2i>();
@@ -157,7 +151,7 @@ public sealed partial class DungeonJob
             if (found)
                 continue;
 
-            _entManager.SpawnEntity(ent, _maps.GridTileToLocal(_gridUid, _grid, tile));
+            _entManager.SpawnEntity(gen.Entity, _maps.GridTileToLocal(_gridUid, _grid, tile));
         }
     }
 }
similarity index 94%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBiome.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs
index 9e5f3bdcfc166216ee3069b37c6a2143e5ef4d6a..ea2e7cfba13e88eeff2d96d7301b0bd487196c9c 100644 (file)
@@ -14,7 +14,7 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="BiomeDunGen"/>
     /// </summary>
-    private async Task PostGen(BiomeDunGen dunGen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(BiomeDunGen dunGen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
         if (!_prototype.TryIndex(dunGen.BiomeTemplate, out var indexedBiome))
             return;
@@ -31,7 +31,7 @@ public sealed partial class DungeonJob
 
             if (reservedTiles.Contains(node))
                 continue;
-            
+
             if (dunGen.TileMask is not null)
             {
                 if (!dunGen.TileMask.Contains(((ContentTileDefinition) _tileDefManager[tileRef.Value.Tile.TypeId]).ID))
similarity index 97%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBiomeMarkerLayer.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs
index fb0eaa01573e5196b5baa7cec30a0911296524d7..abc74ddc4fe3d8e5a359c900a450586b27a185b3 100644 (file)
@@ -15,7 +15,7 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="BiomeMarkerLayerDunGen"/>
     /// </summary>
-    private async Task PostGen(BiomeMarkerLayerDunGen dunGen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(BiomeMarkerLayerDunGen dunGen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
         // If we're adding biome then disable it and just use for markers.
         if (_entManager.EnsureComponent(_gridUid, out BiomeComponent biomeComp))
similarity index 80%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenBoundaryWall.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs
index 84697a56bc7c6333188553523b7d5b76e415e93f..b56a97d40ba81568eefdb5daff884d7cfafd98e0 100644 (file)
@@ -12,27 +12,13 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="BoundaryWallDunGen"/>
     /// </summary>
-    private async Task PostGen(BoundaryWallDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(BoundaryWallDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var protoTileDef) ||
-            !data.Entities.TryGetValue(DungeonDataKey.Walls, out var wall))
-        {
-            _sawmill.Error($"Error finding dungeon data for {nameof(gen)}");
-            return;
-        }
-
-        var tileDef = _tileDefManager[protoTileDef];
+        var tileDef = _tileDefManager[gen.Tile];
         var tiles = new List<(Vector2i Index, Tile Tile)>(dungeon.RoomExteriorTiles.Count);
 
-        if (!data.Entities.TryGetValue(DungeonDataKey.CornerWalls, out var cornerWall))
-        {
-            cornerWall = wall;
-        }
-
-        if (cornerWall == default)
-        {
-            cornerWall = wall;
-        }
+        var wall = gen.Wall;
+        var cornerWall = gen.CornerWall ?? gen.Wall;
 
         // Spawn wall outline
         // - Tiles first
similarity index 69%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCornerClutter.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.CornerClutter.cs
index f7858298504ba90b88686ea891b1db6fc3317b5e..e653ad4a4d0a676aba91a878d5e485925ed37194 100644 (file)
@@ -1,8 +1,6 @@
 using System.Threading.Tasks;
 using Content.Shared.Procedural;
 using Content.Shared.Procedural.PostGeneration;
-using Content.Shared.Storage;
-using Robust.Shared.Physics.Components;
 using Robust.Shared.Random;
 
 namespace Content.Server.Procedural.DungeonJob;
@@ -12,16 +10,15 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="CornerClutterDunGen"/>
     /// </summary>
-    private async Task PostGen(CornerClutterDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(CornerClutterDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.SpawnGroups.TryGetValue(DungeonDataKey.CornerClutter, out var corner))
-        {
-            _sawmill.Error(Environment.StackTrace);
-            return;
-        }
+        var contentsTable = _prototype.Index(gen.Contents);
 
         foreach (var tile in dungeon.CorridorTiles)
         {
+            if (reservedTiles.Contains(tile))
+                continue;
+
             var blocked = _anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask);
 
             if (blocked)
@@ -45,8 +42,8 @@ public sealed partial class DungeonJob
                 if (random.Prob(gen.Chance))
                 {
                     var coords = _maps.GridTileToLocal(_gridUid, _grid, tile);
-                    var protos = EntitySpawnCollection.GetSpawns(_prototype.Index(corner).Entries, random);
-                    _entManager.SpawnEntities(coords, protos);
+                    var protos = contentsTable.Table.GetSpawns(random, _entManager, _prototype);
+                    _entManager.SpawnEntitiesAttachedTo(coords, protos);
                 }
 
                 break;
similarity index 89%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridor.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs
index 8ea79ffe54fd6ebff7b37216403a46f314542770..bf9f910b94d8d29c7b35af5cc7af825bbad56bc5 100644 (file)
@@ -12,14 +12,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="CorridorDunGen"/>
     /// </summary>
-    private async Task PostGen(CorridorDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(CorridorDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto))
-        {
-            LogDataError(typeof(CorridorDunGen));
-            return;
-        }
-
         var entrances = new List<Vector2i>(dungeon.Rooms.Count);
 
         // Grab entrances
@@ -98,7 +92,7 @@ public sealed partial class DungeonJob
         WidenCorridor(dungeon, gen.Width, corridorTiles);
 
         var setTiles = new List<(Vector2i, Tile)>();
-        var tileDef = (ContentTileDefinition) _tileDefManager[tileProto];
+        var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
 
         foreach (var tile in corridorTiles)
         {
similarity index 82%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridorClutter.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs
index cb7c4b210c86ee4d4c7cc8c50b2e9d7d0cd392bf..e0be852733d867b0ac355f2dd2b665f6ab975f66 100644 (file)
@@ -12,10 +12,11 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="CorridorClutterDunGen"/>
     /// </summary>
-    private async Task PostGen(CorridorClutterDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(CorridorClutterDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
         var physicsQuery = _entManager.GetEntityQuery<PhysicsComponent>();
         var count = (int) Math.Ceiling(dungeon.CorridorTiles.Count * gen.Chance);
+        var contents = _prototype.Index(gen.Contents);
 
         while (count > 0)
         {
@@ -42,9 +43,12 @@ public sealed partial class DungeonJob
 
             count--;
 
-            var protos = EntitySpawnCollection.GetSpawns(gen.Contents, random);
+            if (reservedTiles.Contains(tile))
+                continue;
+
+            var protos = _entTable.GetSpawns(contents, random);
             var coords = _maps.ToCenterCoordinates(_gridUid, tile, _grid);
-            _entManager.SpawnEntities(coords, protos);
+            _entManager.SpawnEntitiesAttachedTo(coords, protos);
             await SuspendIfOutOfTime();
 
             if (!ValidateResume())
similarity index 93%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenCorridorDecalSkirting.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs
index 3b516c3fa8a635e968bdef17992352213a08d5da..cd8737e6ec21175f33551889b5b68c4ee3d047b9 100644 (file)
@@ -13,13 +13,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="CorridorDecalSkirtingDunGen"/>
     /// </summary>
-    private async Task PostGen(CorridorDecalSkirtingDunGen decks, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(CorridorDecalSkirtingDunGen decks, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Colors.TryGetValue(DungeonDataKey.Decals, out var color))
-        {
-            _sawmill.Error(Environment.StackTrace);
-        }
-
         var directions = new ValueList<DirectionFlag>(4);
         var pocketDirections = new ValueList<Direction>(4);
         var doorQuery = _entManager.GetEntityQuery<DoorComponent>();
@@ -88,7 +83,7 @@ public sealed partial class DungeonJob
                     {
                         // Decals not being centered biting my ass again
                         var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset);
-                        _decals.TryAddDecal(cDir, gridPos, out _, color: color);
+                        _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color);
                     }
                 }
 
@@ -101,7 +96,7 @@ public sealed partial class DungeonJob
                 {
                     // Decals not being centered biting my ass again
                     var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset);
-                    _decals.TryAddDecal(cDir, gridPos, out _, color: color);
+                    _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color);
                 }
 
                 continue;
@@ -116,7 +111,7 @@ public sealed partial class DungeonJob
                 if (decks.CornerDecals.TryGetValue(dirFlag, out var cDir))
                 {
                     var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset);
-                    _decals.TryAddDecal(cDir, gridPos, out _, color: color);
+                    _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color);
                 }
             }
         }
diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenFill.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenFill.cs
deleted file mode 100644 (file)
index 77c615d..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Numerics;
-using System.Threading.Tasks;
-using Content.Shared.Maps;
-using Content.Shared.Procedural;
-using Content.Shared.Procedural.DungeonGenerators;
-
-namespace Content.Server.Procedural.DungeonJob;
-
-public sealed partial class DungeonJob
-{
-    /// <summary>
-    /// <see cref="FillGridDunGen"/>
-    /// </summary>
-    private async Task<Dungeon> GenerateFillDunGen(FillGridDunGen fill, DungeonData data, HashSet<Vector2i> reservedTiles)
-    {
-        if (!data.Entities.TryGetValue(DungeonDataKey.Fill, out var fillEnt))
-        {
-            LogDataError(typeof(FillGridDunGen));
-            return Dungeon.Empty;
-        }
-
-        var roomTiles = new HashSet<Vector2i>();
-        var tiles = _maps.GetAllTilesEnumerator(_gridUid, _grid);
-
-        while (tiles.MoveNext(out var tileRef))
-        {
-            var tile = tileRef.Value.GridIndices;
-
-            if (reservedTiles.Contains(tile))
-                continue;
-
-            if (fill.AllowedTiles != null && !fill.AllowedTiles.Contains(((ContentTileDefinition) _tileDefManager[tileRef.Value.Tile.TypeId]).ID))
-                continue;
-
-            if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
-                continue;
-
-            var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile);
-            _entManager.SpawnEntity(fillEnt, gridPos);
-
-            roomTiles.Add(tile);
-
-            await SuspendDungeon();
-            if (!ValidateResume())
-                break;
-        }
-
-        var dungeon = new Dungeon();
-        var room = new DungeonRoom(roomTiles, Vector2.Zero, Box2i.Empty, new HashSet<Vector2i>());
-        dungeon.AddRoom(room);
-
-        return dungeon;
-    }
-}
index 33bbeba4b53c700d847acf5b35f554efbd7b4fd3..8eb85e2cb863ebd02b49644606aa1d311bb376cd 100644 (file)
@@ -14,15 +14,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="PrefabDunGen"/>
     /// </summary>
-    private async Task<Dungeon> GeneratePrefabDunGen(Vector2i position, DungeonData data, PrefabDunGen prefab, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task<Dungeon> GeneratePrefabDunGen(Vector2i position, PrefabDunGen prefab, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.Whitelists.TryGetValue(DungeonDataKey.Rooms, out var roomWhitelist))
-        {
-            LogDataError(typeof(PrefabDunGen));
-            return Dungeon.Empty;
-        }
-
         var preset = prefab.Presets[random.Next(prefab.Presets.Count)];
         var gen = _prototype.Index(preset);
 
@@ -50,9 +43,9 @@ public sealed partial class DungeonJob
         {
             var whitelisted = false;
 
-            if (roomWhitelist?.Tags != null)
+            if (prefab.RoomWhitelist?.Tags != null)
             {
-                foreach (var tag in roomWhitelist.Tags)
+                foreach (var tag in prefab.RoomWhitelist.Tags)
                 {
                     if (proto.Tags.Contains(tag))
                     {
@@ -164,6 +157,7 @@ public sealed partial class DungeonJob
 
         // Then for overlaps choose either 1x1 / 3x1
         // Pick a random tile for it and then expand outwards as relevant (weighted towards middle?)
+        var fallbackTile = prefab.FallbackTile;
 
         for (var i = 0; i < chosenPacks.Length; i++)
         {
@@ -181,29 +175,35 @@ public sealed partial class DungeonJob
                 Angle roomRotation = Angle.Zero;
                 Matrix3x2 matty;
 
+                // If no room found then try rotated dimensions
                 if (!roomProtos.TryGetValue(roomDimensions, out var roomProto))
                 {
                     roomDimensions = new Vector2i(roomDimensions.Y, roomDimensions.X);
 
+                    // If nothing at all then no valid rooms, try fallback tile and log it.
                     if (!roomProtos.TryGetValue(roomDimensions, out roomProto))
                     {
-                        matty = Matrix3x2.Multiply(packTransform, dungeonTransform);
-
-                        for (var x = roomSize.Left; x < roomSize.Right; x++)
+                        if (fallbackTile != null)
                         {
-                            for (var y = roomSize.Bottom; y < roomSize.Top; y++)
+                            matty = Matrix3x2.Multiply(packTransform, dungeonTransform);
+
+                            for (var x = roomSize.Left; x < roomSize.Right; x++)
                             {
-                                var index = Vector2.Transform(new Vector2(x, y) + _grid.TileSizeHalfVector - packCenter, matty).Floored();
+                                for (var y = roomSize.Bottom; y < roomSize.Top; y++)
+                                {
+                                    var index = Vector2.Transform(new Vector2(x, y) + _grid.TileSizeHalfVector - packCenter, matty).Floored();
 
-                                if (reservedTiles.Contains(index))
-                                    continue;
+                                    if (reservedTiles.Contains(index))
+                                        continue;
 
-                                tiles.Add((index, new Tile(_tileDefManager[tileProto].TileId)));
+                                    tiles.Add((index, new Tile(_tileDefManager[fallbackTile.Value].TileId)));
+                                }
                             }
+
+                            _maps.SetTiles(_gridUid, _grid, tiles);
+                            tiles.Clear();
                         }
 
-                        _maps.SetTiles(_gridUid, _grid, tiles);
-                        tiles.Clear();
                         _sawmill.Error($"Unable to find room variant for {roomDimensions}, leaving empty.");
                         continue;
                     }
index 6b36d101095c4c7c3b3c4a27c846e2d3cc170f83..dfc09329151a4943a8788536c61c13737fc7d832 100644 (file)
@@ -12,49 +12,43 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="ReplaceTileDunGen"/>
     /// </summary>
-    private async Task<Dungeon> GenerateTileReplacementDunGen(ReplaceTileDunGen gen, DungeonData data, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task GenerateTileReplacementDunGen(ReplaceTileDunGen gen, List<Dungeon> dungeons, HashSet<Vector2i> reservedTiles, Random random)
     {
-        var tiles = _maps.GetAllTilesEnumerator(_gridUid, _grid);
         var replacements = new List<(Vector2i Index, Tile Tile)>();
-        var reserved = new HashSet<Vector2i>();
 
-        while (tiles.MoveNext(out var tileRef))
+        foreach (var dungeon in dungeons)
         {
-            var node = tileRef.Value.GridIndices;
+            foreach (var node in dungeon.AllTiles)
+            {
+                if (reservedTiles.Contains(node))
+                    continue;
 
-            if (reservedTiles.Contains(node))
-                continue;
+                foreach (var layer in gen.Layers)
+                {
+                    var value = layer.Noise.GetNoise(node.X, node.Y);
 
-            foreach (var layer in gen.Layers)
-            {
-                var value = layer.Noise.GetNoise(node.X, node.Y);
+                    if (value < layer.Threshold)
+                        continue;
 
-                if (value < layer.Threshold)
-                    continue;
+                    Tile tile;
 
-                Tile tile;
+                    if (random.Prob(gen.VariantWeight))
+                    {
+                        tile = _tileDefManager.GetVariantTile(_prototype.Index(layer.Tile), random);
+                    }
+                    else
+                    {
+                        tile = new Tile(_prototype.Index(layer.Tile).TileId);
+                    }
 
-                if (random.Prob(gen.VariantWeight))
-                {
-                    tile = _tileDefManager.GetVariantTile(_prototype.Index(layer.Tile), random);
-                }
-                else
-                {
-                    tile = new Tile(_prototype.Index(layer.Tile).TileId);
+                    replacements.Add((node, tile));
+                    break;
                 }
 
-                replacements.Add((node, tile));
-                reserved.Add(node);
-                break;
+                await SuspendDungeon();
             }
 
-            await SuspendDungeon();
+            _maps.SetTiles(_gridUid, _grid, replacements);
         }
-
-        _maps.SetTiles(_gridUid, _grid, replacements);
-        return new Dungeon(new List<DungeonRoom>()
-        {
-            new DungeonRoom(reserved, _position, Box2i.Empty, new HashSet<Vector2i>()),
-        });
     }
 }
similarity index 88%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenDungeonEntrance.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs
index abc52f07c6c97524ce9fbcce0ff9b2a2a7c9745c..ef84fb77df755847daf234761b4bcf539d3e6ac3 100644 (file)
@@ -12,18 +12,12 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="DungeonEntranceDunGen"/>
     /// </summary>
-    private async Task PostGen(DungeonEntranceDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(DungeonEntranceDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Entrance, out var entrance))
-        {
-            LogDataError(typeof(DungeonEntranceDunGen));
-            return;
-        }
-
         var rooms = new List<DungeonRoom>(dungeon.Rooms);
         var roomTiles = new List<Vector2i>();
-        var tileDef = (ContentTileDefinition) _tileDefManager[tileProto];
+        var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
+        var contents = _prototype.Index(gen.Contents);
 
         for (var i = 0; i < gen.Count; i++)
         {
@@ -82,7 +76,7 @@ public sealed partial class DungeonJob
                     var gridCoords = _maps.GridTileToLocal(_gridUid, _grid, tile);
                     // Need to offset the spawn to avoid spawning in the room.
 
-                    foreach (var ent in EntitySpawnCollection.GetSpawns(_prototype.Index(entrance).Entries, random))
+                    foreach (var ent in _entTable.GetSpawns(contents, random))
                     {
                         _entManager.SpawnAtPosition(ent, gridCoords);
                     }
similarity index 66%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenEntranceFlank.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs
index 3a1c7a377938232bbfea7bab4a6a20f948326d96..1788c23cae3962aae8f885ecace50bd76dfc99f9 100644 (file)
@@ -13,18 +13,12 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="EntranceFlankDunGen"/>
     /// </summary>
-    private async Task PostGen(EntranceFlankDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(EntranceFlankDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.EntranceFlank, out var flankProto))
-        {
-            _sawmill.Error($"Unable to get dungeon data for {nameof(gen)}");
-            return;
-        }
-
         var tiles = new List<(Vector2i Index, Tile)>();
-        var tileDef = _tileDefManager[tileProto];
+        var tileDef = _tileDefManager[gen.Tile];
         var spawnPositions = new ValueList<Vector2i>(dungeon.Rooms.Count);
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var room in dungeon.Rooms)
         {
@@ -48,11 +42,10 @@ public sealed partial class DungeonJob
         }
 
         _maps.SetTiles(_gridUid, _grid, tiles);
-        var entGroup = _prototype.Index(flankProto);
 
         foreach (var entrance in spawnPositions)
         {
-            _entManager.SpawnEntities(_maps.GridTileToLocal(_gridUid, _grid, entrance), EntitySpawnCollection.GetSpawns(entGroup.Entries, random));
+            _entManager.SpawnEntitiesAttachedTo(_maps.GridTileToLocal(_gridUid, _grid, entrance), _entTable.GetSpawns(contents, random));
         }
     }
 }
similarity index 88%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.DunGenExterior.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs
index bb2c1cbbbfc6ea4f4cfe09502d123f9d94dc3c81..92237dca2a1678bd6264d364f1ffa7da4b8270df 100644 (file)
@@ -1,10 +1,8 @@
 using System.Threading.Tasks;
-using Content.Server.NPC.Pathfinding;
 using Content.Shared.Maps;
 using Content.Shared.NPC;
 using Content.Shared.Procedural;
 using Content.Shared.Procedural.DungeonGenerators;
-using Robust.Shared.Collections;
 using Robust.Shared.Random;
 using Robust.Shared.Utility;
 
@@ -27,7 +25,6 @@ public sealed partial class DungeonJob
         var startTile = new Vector2i(0, (int) distance).Rotate(angle);
 
         Vector2i? dungeonSpawn = null;
-        var pathfinder = _entManager.System<PathfindingSystem>();
 
         // Gridcast
         SharedPathfindingSystem.GridCast(startTile, position, tile =>
@@ -52,7 +49,7 @@ public sealed partial class DungeonJob
 
         var config = _prototype.Index(dungen.Proto);
         var nextSeed = random.Next();
-        var dungeons = await GetDungeons(dungeonSpawn.Value, config, config.Data, config.Layers, reservedTiles, nextSeed, new Random(nextSeed));
+        var dungeons = await GetDungeons(dungeonSpawn.Value, config, config.Layers, reservedTiles, nextSeed, new Random(nextSeed));
 
         return dungeons;
     }
similarity index 86%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenExternalWindow.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs
index 9a1b44ec91be85a91a24af705ca17c2004b77102..f3906201865a3bf79736e5316617bbd3569a93b5 100644 (file)
@@ -24,15 +24,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="ExternalWindowDunGen"/>
     /// </summary>
-    private async Task PostGen(ExternalWindowDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(ExternalWindowDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Window, out var windowGroup))
-        {
-            _sawmill.Error($"Unable to get dungeon data for {nameof(gen)}");
-            return;
-        }
-
         // Iterate every tile with N chance to spawn windows on that wall per cardinal dir.
         var chance = 0.25 / 3f;
 
@@ -42,7 +35,7 @@ public sealed partial class DungeonJob
         random.Shuffle(validTiles);
 
         var tiles = new List<(Vector2i, Tile)>();
-        var tileDef = _tileDefManager[tileProto];
+        var tileDef = _tileDefManager[gen.Tile];
         var count = Math.Floor(validTiles.Count * chance);
         var index = 0;
         var takenTiles = new HashSet<Vector2i>();
@@ -120,15 +113,13 @@ public sealed partial class DungeonJob
         }
 
         _maps.SetTiles(_gridUid, _grid, tiles);
-        index = 0;
-        var spawnEntry = _prototype.Index(windowGroup);
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var tile in tiles)
         {
             var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile.Item1);
 
-            index += spawnEntry.Entries.Count;
-            _entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(spawnEntry.Entries, random));
+            _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random));
             await SuspendDungeon();
 
             if (!ValidateResume())
diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs
new file mode 100644 (file)
index 0000000..43f453f
--- /dev/null
@@ -0,0 +1,39 @@
+using System.Threading.Tasks;
+using Content.Shared.Procedural;
+using Content.Shared.Procedural.DungeonLayers;
+
+namespace Content.Server.Procedural.DungeonJob;
+
+public sealed partial class DungeonJob
+{
+    /// <summary>
+    /// <see cref="Shared.Procedural.DungeonLayers.FillGridDunGen"/>
+    /// </summary>
+    private async Task GenerateFillDunGen(FillGridDunGen fill, List<Dungeon> dungeons, HashSet<Vector2i> reservedTiles)
+    {
+        foreach (var dungeon in dungeons)
+        {
+            foreach (var tile in dungeon.AllTiles)
+            {
+                if (reservedTiles.Contains(tile))
+                    continue;
+
+                if (!_maps.TryGetTileDef(_grid, tile, out var tileDef))
+                    continue;
+
+                if (fill.AllowedTiles != null && !fill.AllowedTiles.Contains(tileDef.ID))
+                    continue;
+
+                if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
+                    continue;
+
+                var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile);
+                _entManager.SpawnEntity(fill.Entity, gridPos);
+
+                await SuspendDungeon();
+                if (!ValidateResume())
+                    break;
+            }
+        }
+    }
+}
similarity index 98%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs
index 84e7563f338ea9aa4d221ad97ffda2b9010ec440..c57757b4213c6e3a8372a3ed8f0ec61340228122 100644 (file)
@@ -10,9 +10,7 @@ namespace Content.Server.Procedural.DungeonJob;
 
 public sealed partial class DungeonJob
 {
-    /*
-     * Run after the main dungeon generation
-     */
+    // Various helper methods.
 
     private static readonly ProtoId<TagPrototype> WallTag = "Wall";
 
similarity index 84%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenInternalWindow.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs
index d3b8c6d2f5dd0c935d80e40ac521fa2a4c5010a0..c9b148145065ef66f9875f8efa698411eedfa176 100644 (file)
@@ -12,21 +12,14 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="InternalWindowDunGen"/>
     /// </summary>
-    private async Task PostGen(InternalWindowDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(InternalWindowDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Window, out var windowGroup))
-        {
-            _sawmill.Error($"Unable to find dungeon data keys for {nameof(gen)}");
-            return;
-        }
-
         // Iterate every room and check if there's a gap beyond it that leads to another room within N tiles
         // If so then consider windows
         var minDistance = 4;
         var maxDistance = 6;
-        var tileDef = _tileDefManager[tileProto];
-        var window = _prototype.Index(windowGroup);
+        var tileDef = _tileDefManager[gen.Tile];
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var room in dungeon.Rooms)
         {
@@ -90,7 +83,7 @@ public sealed partial class DungeonJob
                     var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile);
                     _maps.SetTile(_gridUid, _grid, tile, _tile.GetVariantTile((ContentTileDefinition) tileDef, random));
 
-                    _entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(window.Entries, random));
+                    _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random));
                 }
 
                 if (validTiles.Count > 0)
similarity index 87%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenJunction.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs
index 700406eb894d92c533769c407cbfdaf0159f9a8a..b491f3df27f75ce99b5be74500b54746cea762a7 100644 (file)
@@ -12,17 +12,10 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="JunctionDunGen"/>
     /// </summary>
-    private async Task PostGen(JunctionDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(JunctionDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Junction, out var junctionProto))
-        {
-            _sawmill.Error($"Dungeon data keys are missing for {nameof(gen)}");
-            return;
-        }
-
-        var tileDef = _tileDefManager[tileProto];
-        var entranceGroup = _prototype.Index(junctionProto);
+        var tileDef = _tileDefManager[gen.Tile];
+        var contents = _prototype.Index(gen.Contents);
 
         // N-wide junctions
         foreach (var tile in dungeon.CorridorTiles)
@@ -123,7 +116,7 @@ public sealed partial class DungeonJob
                         _maps.SetTile(_gridUid, _grid, weh, _tile.GetVariantTile((ContentTileDefinition) tileDef, random));
 
                         var coords = _maps.GridTileToLocal(_gridUid, _grid, weh);
-                        _entManager.SpawnEntities(coords, EntitySpawnCollection.GetSpawns(entranceGroup.Entries, random));
+                        _entManager.SpawnEntitiesAttachedTo(coords, _entTable.GetSpawns(contents, random));
                     }
 
                     break;
similarity index 81%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenMiddleConnection.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs
index 15d0f6342327b2380228e86d3b69dc4cea0acc85..f2ea97b6e67587cf25c63350c17fe5cd368be250 100644 (file)
@@ -13,19 +13,8 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="MiddleConnectionDunGen"/>
     /// </summary>
-    private async Task PostGen(MiddleConnectionDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(MiddleConnectionDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Entrance, out var entranceProto) ||
-            !_prototype.TryIndex(entranceProto, out var entrance))
-        {
-            _sawmill.Error($"Tried to run {nameof(MiddleConnectionDunGen)} without any dungeon data set which is unsupported");
-            return;
-        }
-
-        data.SpawnGroups.TryGetValue(DungeonDataKey.EntranceFlank, out var flankProto);
-        _prototype.TryIndex(flankProto, out var flank);
-
         // Grab all of the room bounds
         // Then, work out connections between them
         var roomBorders = new Dictionary<DungeonRoom, HashSet<Vector2i>>(dungeon.Rooms.Count);
@@ -67,7 +56,9 @@ public sealed partial class DungeonJob
         // TODO: Optional loops
 
         var roomConnections = new Dictionary<DungeonRoom, List<DungeonRoom>>();
-        var tileDef = _tileDefManager[tileProto];
+        var tileDef = _tileDefManager[gen.Tile];
+        _prototype.TryIndex(gen.Flank, out var flankContents);
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var (room, border) in roomBorders)
         {
@@ -118,16 +109,16 @@ public sealed partial class DungeonJob
                     width--;
                     _maps.SetTile(_gridUid, _grid, node, _tile.GetVariantTile((ContentTileDefinition) tileDef, random));
 
-                    if (flank != null && nodeDistances.Count - i <= 2)
+                    if (flankContents != null && nodeDistances.Count - i <= 2)
                     {
-                        _entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(flank.Entries, random));
+                        _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(flankContents, random));
                     }
                     else
                     {
                         // Iterate neighbors and check for blockers, if so bulldoze
                         ClearDoor(dungeon, _grid, node);
 
-                        _entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(entrance.Entries, random));
+                        _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random));
                     }
 
                     if (width == 0)
similarity index 90%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.MobDunGen.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs
index 150849d2c51919c001deac2ec470b306095e664a..cf1c239b0e089ae8db494b17077a6c3c2fc42c0c 100644 (file)
@@ -13,6 +13,9 @@ namespace Content.Server.Procedural.DungeonJob;
 
 public sealed partial class DungeonJob
 {
+    /// <summary>
+    /// <see cref="MobsDunGen"/>
+    /// </summary>
     private async Task PostGen(
         MobsDunGen gen,
         Dungeon dungeon,
@@ -21,8 +24,8 @@ public sealed partial class DungeonJob
         var availableRooms = new ValueList<DungeonRoom>();
         availableRooms.AddRange(dungeon.Rooms);
         var availableTiles = new ValueList<Vector2i>(dungeon.AllTiles);
+        var contents = _prototype.Index(gen.Contents);
 
-        var entities = EntitySpawnCollection.GetSpawns(gen.Groups, random);
         var count = random.Next(gen.MinCount, gen.MaxCount + 1);
         var npcs = _entManager.System<NPCSystem>();
 
@@ -38,6 +41,8 @@ public sealed partial class DungeonJob
                     continue;
                 }
 
+                var entities = _entTable.GetSpawns(contents, random);
+
                 foreach (var ent in entities)
                 {
                     var uid = _entManager.SpawnAtPosition(ent, _maps.GridTileToLocal(_gridUid, _grid, tile));
diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.PostGenDungeonConnector.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.PostGenDungeonConnector.cs
deleted file mode 100644 (file)
index 917b1ff..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Server.Procedural.DungeonJob;
-
-public sealed partial class DungeonJob
-{
-
-}
similarity index 59%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenRoomEntrance.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs
index 09d223e86cf5f281809a3d27673c7f9b06f8f566..a4a01b5f0b072a4f26a05fc5eb66792bba5dae35 100644 (file)
@@ -12,23 +12,19 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="RoomEntranceDunGen"/>
     /// </summary>
-    private async Task PostGen(RoomEntranceDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(RoomEntranceDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
-            !data.SpawnGroups.TryGetValue(DungeonDataKey.Entrance, out var entranceProtos) ||
-            !_prototype.TryIndex(entranceProtos, out var entranceIn))
-        {
-            LogDataError(typeof(RoomEntranceDunGen));
-            return;
-        }
-
         var setTiles = new List<(Vector2i, Tile)>();
-        var tileDef = _tileDefManager[tileProto];
+        var tileDef = _tileDefManager[gen.Tile];
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var room in dungeon.Rooms)
         {
             foreach (var entrance in room.Entrances)
             {
+                if (reservedTiles.Contains(entrance))
+                    continue;
+
                 setTiles.Add((entrance, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)));
             }
         }
@@ -39,9 +35,17 @@ public sealed partial class DungeonJob
         {
             foreach (var entrance in room.Entrances)
             {
-                _entManager.SpawnEntities(
+                if (reservedTiles.Contains(entrance))
+                    continue;
+
+                _entManager.SpawnEntitiesAttachedTo(
                     _maps.GridTileToLocal(_gridUid, _grid, entrance),
-                    EntitySpawnCollection.GetSpawns(entranceIn.Entries, random));
+                    _entTable.GetSpawns(contents, random));
+
+                await SuspendDungeon();
+
+                if (!ValidateResume())
+                    return;
             }
         }
     }
similarity index 85%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenSplineDungeonConnector.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs
index 8fe2f36665f9548941ec3b2775778a7dd87d922c..a131efd3534b5900c3bf4da2904219e789d58d8a 100644 (file)
@@ -15,25 +15,14 @@ public sealed partial class DungeonJob
     /// </summary>
     private async Task<Dungeon> PostGen(
         SplineDungeonConnectorDunGen gen,
-        DungeonData data,
         List<Dungeon> dungeons,
         HashSet<Vector2i> reservedTiles,
         Random random)
     {
-        // TODO: The path itself use the tile
-        // Widen it randomly (probably for each tile offset it by some changing amount).
-
         // NOOP
         if (dungeons.Count <= 1)
             return Dungeon.Empty;
 
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var fallback) ||
-            !data.Tiles.TryGetValue(DungeonDataKey.WidenTile, out var widen))
-        {
-            LogDataError(typeof(SplineDungeonConnectorDunGen));
-            return Dungeon.Empty;
-        }
-
         var nodes = new List<Vector2i>();
 
         foreach (var dungeon in dungeons)
@@ -57,7 +46,8 @@ public sealed partial class DungeonJob
         var tiles = new List<(Vector2i Index, Tile Tile)>();
         var pathfinding = _entManager.System<PathfindingSystem>();
         var allTiles = new HashSet<Vector2i>();
-        var fallbackTile = new Tile(_prototype.Index(fallback).TileId);
+        var pathTile = new Tile(_prototype.Index(gen.Tile).TileId);
+        var widen = _prototype.Index(gen.WidenTile ?? gen.Tile);
 
         foreach (var pair in tree)
         {
@@ -112,7 +102,7 @@ public sealed partial class DungeonJob
 
                 if (random.Prob(0.9f))
                 {
-                    tile = new Tile(_prototype.Index(widen).TileId);
+                    tile = new Tile(widen.TileId);
                 }
                 else
                 {
@@ -132,7 +122,7 @@ public sealed partial class DungeonJob
                     continue;
 
                 allTiles.Add(node);
-                tiles.Add((node, fallbackTile));
+                tiles.Add((node, pathTile));
             }
 
             _maps.SetTiles(_gridUid, _grid, tiles);
similarity index 52%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenWallMount.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs
index d5c8587ea95f1376b6bb2636d692595b23d25e73..e01fa9b9473d78bd38af35b93432d67a49f31561 100644 (file)
@@ -1,4 +1,5 @@
 using System.Threading.Tasks;
+using Content.Shared.Maps;
 using Content.Shared.Procedural;
 using Content.Shared.Procedural.PostGeneration;
 using Content.Shared.Storage;
@@ -11,25 +12,13 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="WallMountDunGen"/>
     /// </summary>
-    private async Task PostGen(WallMountDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(WallMountDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto))
-        {
-            _sawmill.Error($"Tried to run {nameof(WallMountDunGen)} without any dungeon data set which is unsupported");
-            return;
-        }
-
-        var tileDef = _prototype.Index(tileProto);
-        if (!data.SpawnGroups.TryGetValue(DungeonDataKey.WallMounts, out var spawnProto))
-        {
-            // caves can have no walls
-            return;
-        }
-
         var checkedTiles = new HashSet<Vector2i>();
         var allExterior = new HashSet<Vector2i>(dungeon.CorridorExteriorTiles);
         allExterior.UnionWith(dungeon.RoomExteriorTiles);
-        var count = 0;
+        var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
+        var contents = _prototype.Index(gen.Contents);
 
         foreach (var neighbor in allExterior)
         {
@@ -40,21 +29,18 @@ public sealed partial class DungeonJob
             if (!random.Prob(gen.Prob) || !checkedTiles.Add(neighbor))
                 continue;
 
+            if (reservedTiles.Contains(neighbor))
+                continue;
+
             _maps.SetTile(_gridUid, _grid, neighbor, _tile.GetVariantTile(tileDef, random));
             var gridPos = _maps.GridTileToLocal(_gridUid, _grid, neighbor);
-            var protoNames = EntitySpawnCollection.GetSpawns(_prototype.Index(spawnProto).Entries, random);
-
-            _entManager.SpawnEntities(gridPos, protoNames);
-            count += protoNames.Count;
+            var protoNames = _entTable.GetSpawns(contents, random);
 
-            if (count > 20)
-            {
-                count -= 20;
-                await SuspendDungeon();
+            _entManager.SpawnEntitiesAttachedTo(gridPos, protoNames);
 
-                if (!ValidateResume())
-                    return;
-            }
+            await SuspendDungeon();
+            if (!ValidateResume())
+                return;
         }
     }
 }
similarity index 93%
rename from Content.Server/Procedural/DungeonJob/DungeonJob.PostGenWorm.cs
rename to Content.Server/Procedural/DungeonJob/DungeonJob.Worm.cs
index 6fd00e5482485330b0af9b26672b6b0e7bc2c805..7367b9147ab5dacd012b5ca893fa7a7d6ddcbeaf 100644 (file)
@@ -1,5 +1,6 @@
 using System.Linq;
 using System.Threading.Tasks;
+using Content.Shared.Maps;
 using Content.Shared.Procedural;
 using Content.Shared.Procedural.PostGeneration;
 using Robust.Shared.Collections;
@@ -14,19 +15,14 @@ public sealed partial class DungeonJob
     /// <summary>
     /// <see cref="WormCorridorDunGen"/>
     /// </summary>
-    private async Task PostGen(WormCorridorDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
+    private async Task PostGen(WormCorridorDunGen gen, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
     {
-        if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) || !_prototype.TryIndex(tileProto, out var tileDef))
-        {
-            _sawmill.Error($"Tried to run {nameof(WormCorridorDunGen)} without any dungeon data set which is unsupported");
-            return;
-        }
-
         var networks = new List<(Vector2i Start, HashSet<Vector2i> Network)>();
 
         // List of places to start from.
         var worm = new ValueList<Vector2i>();
         var startAngles = new Dictionary<Vector2i, Angle>();
+        var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
 
         foreach (var room in dungeon.Rooms)
         {
index cdb5eb080550ae2a3b7de12aa170eebf5e3b801a..58583f833d3825676088d1524fe5b9c25eedb2cf 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Content.Server.Decals;
@@ -6,6 +7,7 @@ using Content.Server.NPC.HTN;
 using Content.Server.NPC.Systems;
 using Content.Server.Shuttles.Systems;
 using Content.Shared.Construction.EntitySystems;
+using Content.Shared.EntityTable;
 using Content.Shared.Maps;
 using Content.Shared.Procedural;
 using Content.Shared.Procedural.DungeonGenerators;
@@ -37,6 +39,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
     private readonly DecalSystem _decals;
     private readonly DungeonSystem _dungeon;
     private readonly EntityLookupSystem _lookup;
+    private readonly EntityTableSystem _entTable;
     private readonly TagSystem _tags;
     private readonly TileSystem _tile;
     private readonly SharedMapSystem _maps;
@@ -88,6 +91,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
         _tile = tile;
         _tags = _entManager.System<TagSystem>();
         _maps = _entManager.System<SharedMapSystem>();
+        _entTable = _entManager.System<EntityTableSystem>();
         _transform = transform;
 
         _physicsQuery = _entManager.GetEntityQuery<PhysicsComponent>();
@@ -108,13 +112,20 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
     private async Task<List<Dungeon>> GetDungeons(
         Vector2i position,
         DungeonConfig config,
-        DungeonData data,
         List<IDunGenLayer> layers,
         HashSet<Vector2i> reservedTiles,
         int seed,
-        Random random)
+        Random random,
+        List<Dungeon>? existing = null)
     {
         var dungeons = new List<Dungeon>();
+
+        // Don't pass dungeons back up the "stack". They are ref types though it's a caller problem if they start trying to mutate it.
+        if (existing != null)
+        {
+            dungeons.AddRange(existing);
+        }
+
         var count = random.Next(config.MinCount, config.MaxCount + 1);
 
         for (var i = 0; i < count; i++)
@@ -123,10 +134,25 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
 
             foreach (var layer in layers)
             {
-                await RunLayer(dungeons, data, position, layer, reservedTiles, seed, random);
+                await RunLayer(dungeons, position, layer, reservedTiles, seed, random);
 
                 if (config.ReserveTiles)
                 {
+                    // Remove any dungeons passed in so we don't interfere with them
+                    // This is kinda goofy but okay for now.
+                    if (existing != null)
+                    {
+                        for (var j = 0; j < dungeons.Count; j++)
+                        {
+                            var dung = dungeons[j];
+
+                            if (existing.Contains(dung))
+                            {
+                                dungeons.RemoveSwap(j);
+                            }
+                        }
+                    }
+
                     foreach (var dungeon in dungeons)
                     {
                         reservedTiles.UnionWith(dungeon.AllTiles);
@@ -152,7 +178,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
         // Tiles we can no longer generate on due to being reserved elsewhere.
         var reservedTiles = new HashSet<Vector2i>();
 
-        var dungeons = await GetDungeons(position, _gen, _gen.Data, _gen.Layers, reservedTiles, _seed, random);
+        var dungeons = await GetDungeons(position, _gen, _gen.Layers, reservedTiles, _seed, random);
         // To make it slightly more deterministic treat this RNG as separate ig.
 
         // Post-processing after finishing loading.
@@ -181,7 +207,6 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
 
     private async Task RunLayer(
         List<Dungeon> dungeons,
-        DungeonData data,
         Vector2i position,
         IDunGenLayer layer,
         HashSet<Vector2i> reservedTiles,
@@ -200,52 +225,52 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
         switch (layer)
         {
             case AutoCablingDunGen cabling:
-                await PostGen(cabling, data, dungeons[^1], reservedTiles, random);
+                await PostGen(cabling, dungeons[^1], reservedTiles, random);
                 break;
             case BiomeMarkerLayerDunGen markerPost:
-                await PostGen(markerPost, data, dungeons[^1], reservedTiles, random);
+                await PostGen(markerPost, dungeons[^1], reservedTiles, random);
                 break;
             case BiomeDunGen biome:
-                await PostGen(biome, data, dungeons[^1], reservedTiles, random);
+                await PostGen(biome, dungeons[^1], reservedTiles, random);
                 break;
             case BoundaryWallDunGen boundary:
-                await PostGen(boundary, data, dungeons[^1], reservedTiles, random);
+                await PostGen(boundary, dungeons[^1], reservedTiles, random);
                 break;
             case CornerClutterDunGen clutter:
-                await PostGen(clutter, data, dungeons[^1], reservedTiles, random);
+                await PostGen(clutter, dungeons[^1], reservedTiles, random);
                 break;
             case CorridorClutterDunGen corClutter:
-                await PostGen(corClutter, data, dungeons[^1], reservedTiles, random);
+                await PostGen(corClutter, dungeons[^1], reservedTiles, random);
                 break;
             case CorridorDunGen cordor:
-                await PostGen(cordor, data, dungeons[^1], reservedTiles, random);
+                await PostGen(cordor, dungeons[^1], reservedTiles, random);
                 break;
             case CorridorDecalSkirtingDunGen decks:
-                await PostGen(decks, data, dungeons[^1], reservedTiles, random);
+                await PostGen(decks, dungeons[^1], reservedTiles, random);
                 break;
             case EntranceFlankDunGen flank:
-                await PostGen(flank, data, dungeons[^1], reservedTiles, random);
+                await PostGen(flank, dungeons[^1], reservedTiles, random);
                 break;
             case ExteriorDunGen exterior:
                 dungeons.AddRange(await GenerateExteriorDungen(position, exterior, reservedTiles, random));
                 break;
             case FillGridDunGen fill:
-                dungeons.Add(await GenerateFillDunGen(fill, data, reservedTiles));
+                await GenerateFillDunGen(fill, dungeons, reservedTiles);
                 break;
             case JunctionDunGen junc:
-                await PostGen(junc, data, dungeons[^1], reservedTiles, random);
+                await PostGen(junc, dungeons[^1], reservedTiles, random);
                 break;
             case MiddleConnectionDunGen dordor:
-                await PostGen(dordor, data, dungeons[^1], reservedTiles, random);
+                await PostGen(dordor, dungeons[^1], reservedTiles, random);
                 break;
             case DungeonEntranceDunGen entrance:
-                await PostGen(entrance, data, dungeons[^1], reservedTiles, random);
+                await PostGen(entrance, dungeons[^1], reservedTiles, random);
                 break;
             case ExternalWindowDunGen externalWindow:
-                await PostGen(externalWindow, data, dungeons[^1], reservedTiles, random);
+                await PostGen(externalWindow, dungeons[^1], reservedTiles, random);
                 break;
             case InternalWindowDunGen internalWindow:
-                await PostGen(internalWindow, data, dungeons[^1], reservedTiles, random);
+                await PostGen(internalWindow, dungeons[^1], reservedTiles, random);
                 break;
             case MobsDunGen mob:
                 await PostGen(mob, dungeons[^1], random);
@@ -263,31 +288,40 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
                 await PostGen(ore, dungeons[^1], random);
                 break;
             case PrefabDunGen prefab:
-                dungeons.Add(await GeneratePrefabDunGen(position, data, prefab, reservedTiles, random));
+                dungeons.Add(await GeneratePrefabDunGen(position, prefab, reservedTiles, random));
                 break;
             case PrototypeDunGen prototypo:
                 var groupConfig = _prototype.Index(prototypo.Proto);
                 position = (position + random.NextPolarVector2(groupConfig.MinOffset, groupConfig.MaxOffset)).Floored();
 
-                var dataCopy = groupConfig.Data.Clone();
-                dataCopy.Apply(data);
+                switch (prototypo.InheritDungeons)
+                {
+                    case DungeonInheritance.All:
+                        dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random, existing: dungeons));
+                        break;
+                    case DungeonInheritance.Last:
+                        dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random, existing: dungeons.GetRange(dungeons.Count - 1, 1)));
+                        break;
+                    case DungeonInheritance.None:
+                        dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random));
+                        break;
+                }
 
-                dungeons.AddRange(await GetDungeons(position, groupConfig, dataCopy, groupConfig.Layers, reservedTiles, seed, random));
                 break;
             case ReplaceTileDunGen replace:
-                dungeons.Add(await GenerateTileReplacementDunGen(replace, data, reservedTiles, random));
+                await GenerateTileReplacementDunGen(replace, dungeons, reservedTiles, random);
                 break;
             case RoomEntranceDunGen rEntrance:
-                await PostGen(rEntrance, data, dungeons[^1], reservedTiles, random);
+                await PostGen(rEntrance, dungeons[^1], reservedTiles, random);
                 break;
             case SplineDungeonConnectorDunGen spline:
-                dungeons.Add(await PostGen(spline, data, dungeons, reservedTiles, random));
+                dungeons.Add(await PostGen(spline, dungeons, reservedTiles, random));
                 break;
             case WallMountDunGen wall:
-                await PostGen(wall, data, dungeons[^1], reservedTiles, random);
+                await PostGen(wall, dungeons[^1], reservedTiles, random);
                 break;
             case WormCorridorDunGen worm:
-                await PostGen(worm, data, dungeons[^1], reservedTiles, random);
+                await PostGen(worm, dungeons[^1], reservedTiles, random);
                 break;
             default:
                 throw new NotImplementedException();
index ff499e67604540beffd8d4b641c10c4010f5bf6f..eab8b87ad719793479d7f0c699bb30f4221885de 100644 (file)
@@ -9,6 +9,12 @@ public sealed class EntityTableSystem : EntitySystem
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
 
+    public IEnumerable<EntProtoId> GetSpawns(EntityTablePrototype entTableProto, System.Random? rand = null)
+    {
+        // convenient
+        return GetSpawns(entTableProto.Table, rand);
+    }
+
     public IEnumerable<EntProtoId> GetSpawns(EntityTableSelector? table, System.Random? rand = null)
     {
         if (table == null)
index 2c2adc18030c336d2acd8ae21cc64f3ce1fee32a..7c84b1a6a3b22d484979a5d66eba91bec9b6e12f 100644 (file)
@@ -5,12 +5,6 @@ namespace Content.Shared.Procedural;
 [Virtual, DataDefinition]
 public partial class DungeonConfig
 {
-    /// <summary>
-    /// <see cref="Data"/>
-    /// </summary>
-    [DataField]
-    public DungeonData Data = DungeonData.Empty;
-
     /// <summary>
     /// The secret sauce, procedural generation layers that get run.
     /// </summary>
diff --git a/Content.Shared/Procedural/DungeonData.cs b/Content.Shared/Procedural/DungeonData.cs
deleted file mode 100644 (file)
index f15d974..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-using System.Linq;
-using Content.Shared.Maps;
-using Content.Shared.Storage;
-using Content.Shared.Whitelist;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Procedural;
-
-/// <summary>
-/// Used to set dungeon values for all layers.
-/// </summary>
-/// <remarks>
-/// This lets us share data between different dungeon configs without having to repeat entire configs.
-/// </remarks>
-[DataRecord]
-public sealed partial class DungeonData
-{
-    // I hate this but it also significantly reduces yaml bloat if we add like 10 variations on the same set of layers
-    // e.g. science rooms, engi rooms, cargo rooms all under PlanetBase for example.
-    // without having to do weird nesting. It also means we don't need to copy-paste the same prototype across several layers
-    // The alternative is doing like,
-    // 2 layer prototype, 1 layer with the specified data, 3 layer prototype, 2 layers with specified data, etc.
-    // As long as we just keep the code clean over time it won't be bad to maintain.
-
-    public static DungeonData Empty = new();
-
-    public Dictionary<DungeonDataKey, Color> Colors = new();
-    public Dictionary<DungeonDataKey, EntProtoId> Entities = new();
-    public Dictionary<DungeonDataKey, ProtoId<EntitySpawnEntryPrototype>> SpawnGroups = new();
-    public Dictionary<DungeonDataKey, ProtoId<ContentTileDefinition>> Tiles = new();
-    public Dictionary<DungeonDataKey, EntityWhitelist> Whitelists = new();
-
-    /// <summary>
-    /// Applies the specified data to this data.
-    /// </summary>
-    public void Apply(DungeonData data)
-    {
-        // Copy-paste moment.
-        foreach (var color in data.Colors)
-        {
-            Colors[color.Key] = color.Value;
-        }
-
-        foreach (var color in data.Entities)
-        {
-            Entities[color.Key] = color.Value;
-        }
-
-        foreach (var color in data.SpawnGroups)
-        {
-            SpawnGroups[color.Key] = color.Value;
-        }
-
-        foreach (var color in data.Tiles)
-        {
-            Tiles[color.Key] = color.Value;
-        }
-
-        foreach (var color in data.Whitelists)
-        {
-            Whitelists[color.Key] = color.Value;
-        }
-    }
-
-    public DungeonData Clone()
-    {
-        return new DungeonData
-        {
-            // Only shallow clones but won't matter for DungeonJob purposes.
-            Colors = Colors.ShallowClone(),
-            Entities = Entities.ShallowClone(),
-            SpawnGroups = SpawnGroups.ShallowClone(),
-            Tiles = Tiles.ShallowClone(),
-            Whitelists = Whitelists.ShallowClone(),
-        };
-    }
-}
-
-public enum DungeonDataKey : byte
-{
-    // Colors
-    Decals,
-
-    // Entities
-    Cabling,
-    CornerWalls,
-    Fill,
-    Junction,
-    Walls,
-
-    // SpawnGroups
-    CornerClutter,
-    Entrance,
-    EntranceFlank,
-    WallMounts,
-    Window,
-
-    // Tiles
-    FallbackTile,
-    WidenTile,
-
-    // Whitelists
-    Rooms,
-}
index aeb24d0144820ff5e5e919f3f7d0e869e8ca95db..a9a6d12de4b501fdd82d5752cf5d7f163317da93 100644 (file)
@@ -1,3 +1,5 @@
+using Content.Shared.Maps;
+using Content.Shared.Whitelist;
 using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Procedural.DungeonGenerators;
@@ -6,10 +8,6 @@ namespace Content.Shared.Procedural.DungeonGenerators;
 /// Places rooms in pre-selected pack layouts. Chooses rooms from the specified whitelist.
 /// </summary>
 /// <remarks>
-/// DungeonData keys are:
-/// - FallbackTile
-/// - Rooms
-/// </remarks>
 public sealed partial class PrefabDunGen : IDunGenLayer
 {
     /// <summary>
@@ -17,4 +15,10 @@ public sealed partial class PrefabDunGen : IDunGenLayer
     /// </summary>
     [DataField(required: true)]
     public List<ProtoId<DungeonPresetPrototype>> Presets = new();
+
+    [DataField]
+    public EntityWhitelist? RoomWhitelist;
+
+    [DataField]
+    public ProtoId<ContentTileDefinition>? FallbackTile;
 }
index 2772c97977bc0fe7ff9a6acd4194336dc2762617..89a4ab216a1acf3561b37d4c7a799f8cc5e6b063 100644 (file)
@@ -8,6 +8,30 @@ namespace Content.Shared.Procedural.DungeonGenerators;
 /// </summary>
 public sealed partial class PrototypeDunGen : IDunGenLayer
 {
+    /// <summary>
+    /// Should we pass in the current level's dungeons to the prototype.
+    /// </summary>
+    [DataField]
+    public DungeonInheritance InheritDungeons = DungeonInheritance.None;
+
     [DataField(required: true)]
     public ProtoId<DungeonConfigPrototype> Proto;
 }
+
+public enum DungeonInheritance : byte
+{
+    /// <summary>
+    /// Don't inherit any of the current layer's dungeons for this <see cref="PrototypeDunGen"/>
+    /// </summary>
+    None,
+
+    /// <summary>
+    /// Inherit only the last dungeon ran.
+    /// </summary>
+    Last,
+
+    /// <summary>
+    /// Inherit all of the current layer's dungeons.
+    /// </summary>
+    All,
+}
similarity index 79%
rename from Content.Shared/Procedural/DungeonGenerators/FillGridDunGen.cs
rename to Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs
index 53c7dd8c66fc1fc8c8d1e9f9c38555098b43dabe..363de0a511668fe7b11846ff2119cae3c3c4f294 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Shared.Maps;
 using Robust.Shared.Prototypes;
 
-namespace Content.Shared.Procedural.DungeonGenerators;
+namespace Content.Shared.Procedural.DungeonLayers;
 
 /// <summary>
 /// Fills unreserved tiles with the specified entity prototype.
@@ -17,4 +17,7 @@ public sealed partial class FillGridDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public HashSet<ProtoId<ContentTileDefinition>>? AllowedTiles;
+
+    [DataField(required: true)]
+    public EntProtoId Entity;
 }
index 30b502efe073e269523645c6631da8105e460073..5525341eb9896595605668c7c37658df5bd5265c 100644 (file)
@@ -1,4 +1,6 @@
+using Content.Shared.EntityTable;
 using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Procedural.DungeonLayers;
 
@@ -17,5 +19,5 @@ public sealed partial class MobsDunGen : IDunGenLayer
     public int MaxCount = 1;
 
     [DataField(required: true)]
-    public List<EntitySpawnEntry> Groups = new();
+    public ProtoId<EntityTablePrototype> Contents;
 }
index 1947779768160af5036c432cdbed9d65ac5ab8d9..e4968b6e429087c25387bfd16371fe62eb643995 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Parallax.Biomes;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Shared.Procedural.Loot;
@@ -8,6 +9,6 @@ namespace Content.Shared.Procedural.Loot;
 /// </summary>
 public sealed partial class BiomeTemplateLoot : IDungeonLoot
 {
-    [DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<BiomeTemplatePrototype>))]
-    public string Prototype = string.Empty;
+    [DataField("proto", required: true)]
+    public ProtoId<BiomeTemplatePrototype> Prototype = string.Empty;
 }
index 5afad7edb18f5fdaeaf34514a6e8794e4db80ca8..f6588aee415a9334b2559e174962e73834be0432 100644 (file)
@@ -1,10 +1,12 @@
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Runs cables throughout the dungeon.
 /// </summary>
-/// <remarks>
-/// DungeonData keys are:
-/// - Cabling
-/// </remarks>
-public sealed partial class AutoCablingDunGen : IDunGenLayer;
+public sealed partial class AutoCablingDunGen : IDunGenLayer
+{
+    [DataField(required: true)]
+    public EntProtoId Entity;
+}
index 4151527f8a07c66cf7f142cdfff872bbe256a68b..19e9d67343d199157092c32808aa634124401a94 100644 (file)
@@ -1,3 +1,6 @@
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
@@ -13,6 +16,15 @@ public sealed partial class BoundaryWallDunGen : IDunGenLayer
 {
     [DataField]
     public BoundaryWallFlags Flags = BoundaryWallFlags.Corridors | BoundaryWallFlags.Rooms;
+
+    [DataField(required: true)]
+    public EntProtoId Wall;
+
+    [DataField]
+    public EntProtoId? CornerWall;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
 }
 
 [Flags]
index 2a904281c8036e95ded1a137071a321253f2efbf..dc048cfb55a788d5a78d2fa01f72ebd6f38d35f2 100644 (file)
@@ -1,14 +1,17 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Spawns entities inside corners.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - CornerClutter
-/// </remarks>
 public sealed partial class CornerClutterDunGen : IDunGenLayer
 {
     [DataField]
     public float Chance = 0.50f;
+
+    [DataField(required:true)]
+    public ProtoId<EntityTablePrototype> Contents = new();
 }
index 5b397b40dfc1852539b0c380ad683b708f4f90d0..8e3d092d1bafdab435548d229a818341cd41556d 100644 (file)
@@ -1,4 +1,6 @@
+using Content.Shared.EntityTable;
 using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Procedural.PostGeneration;
 
@@ -14,5 +16,5 @@ public sealed partial class CorridorClutterDunGen : IDunGenLayer
     /// The default starting bulbs
     /// </summary>
     [DataField(required: true)]
-    public List<EntitySpawnEntry> Contents = new();
+    public ProtoId<EntityTablePrototype> Contents;
 }
index e6090436555561d114c884fb9387daf5e0704c18..fac9dc97da0e7cdd3e1d6b5b1be30a43e9be4a93 100644 (file)
@@ -1,7 +1,3 @@
-using Content.Shared.Decals;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
-
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
@@ -26,4 +22,10 @@ public sealed partial class CorridorDecalSkirtingDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public Dictionary<DirectionFlag, string> CornerDecals = new();
+
+    /// <summary>
+    /// Optional color to apply to the decals.
+    /// </summary>
+    [DataField]
+    public Color? Color;
 }
index 6d75cd9cb2bda3781d20f7a979cc799e454bf2e9..b36c6f9b64ebf17db465465eafeb027ad479de5b 100644 (file)
@@ -1,12 +1,11 @@
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Connects room entrances via corridor segments.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - FallbackTile
-/// </remarks>
 public sealed partial class CorridorDunGen : IDunGenLayer
 {
     /// <summary>
@@ -23,4 +22,7 @@ public sealed partial class CorridorDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public float Width = 3f;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
 }
index 40cc95f5fc90eee22d31d93aa5a71b2ae190753f..406054cfdc1f8bcc61ecba669e129b382f835eb8 100644 (file)
@@ -1,13 +1,13 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Selects [count] rooms and places external doors to them.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - Entrance
-/// - FallbackTile
-/// </remarks>
 public sealed partial class DungeonEntranceDunGen : IDunGenLayer
 {
     /// <summary>
@@ -15,4 +15,10 @@ public sealed partial class DungeonEntranceDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public int Count = 1;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
 }
index 27baa48ec628c7c57c143714acf2cdedf112f42e..f9be6caf6ae3a1938c593bb660b042c5da72bd39 100644 (file)
@@ -1,11 +1,17 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Spawns entities on either side of an entrance.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - FallbackTile
-/// -
-/// </remarks>
-public sealed partial class EntranceFlankDunGen : IDunGenLayer;
+public sealed partial class EntranceFlankDunGen : IDunGenLayer
+{
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents = new();
+}
index 0b29344b90b6066b97f39e0054c4c735c5cf1d56..fc992ea7b8928ece434b9d26d2cf4a6db2696bee 100644 (file)
@@ -1,11 +1,18 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// If external areas are found will try to generate windows.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - EntranceFlank
-/// - FallbackTile
-/// </remarks>
-public sealed partial class ExternalWindowDunGen : IDunGenLayer;
+public sealed partial class ExternalWindowDunGen : IDunGenLayer
+{
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
+}
index 11b1c6a785a6b448d1a2af704d7336488cea32d3..22a4839ce2042af54a6401ade94a09f524d378e0 100644 (file)
@@ -1,11 +1,17 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// If internal areas are found will try to generate windows.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - FallbackTile
-/// - Window
-/// </remarks>
-public sealed partial class InternalWindowDunGen : IDunGenLayer;
+public sealed partial class InternalWindowDunGen : IDunGenLayer
+{
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
+}
index 899f2716216833d6d24f7a4d10ffda09c0f92b51..f18f63ddfe6ad9edafd36be652f8722f226cf606 100644 (file)
@@ -1,13 +1,13 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Places the specified entities at junction areas.
 /// </summary>
-/// <remarks>
-/// Dungeon data keys are:
-/// - Entrance
-/// - FallbackTile
-/// </remarks>
 public sealed partial class JunctionDunGen : IDunGenLayer
 {
     /// <summary>
@@ -15,4 +15,10 @@ public sealed partial class JunctionDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public int Width = 3;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
 }
index a5758c14989368f170a28df706f53589b67960f4..a1040fef9bcb0667570c7c0bb09ea15d637e90be 100644 (file)
@@ -1,3 +1,8 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
@@ -16,4 +21,13 @@ public sealed partial class MiddleConnectionDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public int Count = 1;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
+
+    [DataField]
+    public ProtoId<EntityTablePrototype>? Flank;
 }
index d3b5672dcb015cb0344977ae3123657a81fe1568..1436f7473d9718a7cc4efc535da32e92cc17fc8f 100644 (file)
@@ -1,11 +1,18 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
 /// Places tiles / entities onto room entrances.
 /// </summary>
-/// <remarks>
-/// DungeonData keys are:
-/// - Entrance
-/// - FallbackTile
-/// </remarks>
-public sealed partial class RoomEntranceDunGen : IDunGenLayer;
+public sealed partial class RoomEntranceDunGen : IDunGenLayer
+{
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField]
+    public ProtoId<EntityTablePrototype> Contents;
+}
index ec8349c671bda0bd3e1339c087ea6270d33aaa02..d2f5a2126a74dcb214d4fe41c7d3421a01ddcda4 100644 (file)
@@ -1,3 +1,6 @@
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
@@ -5,6 +8,12 @@ namespace Content.Shared.Procedural.PostGeneration;
 /// </summary>
 public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer
 {
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField]
+    public ProtoId<ContentTileDefinition>? WidenTile;
+
     /// <summary>
     /// Will divide the distance between the start and end points so that no subdivision is more than these metres away.
     /// </summary>
index a5c790cb22fc147a57302037d083b5f388c4d083..2a47146ca6e1311394319d8f724a0e4643529a7a 100644 (file)
@@ -1,3 +1,8 @@
+using Content.Shared.EntityTable;
+using Content.Shared.Maps;
+using Content.Shared.Storage;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 /// <summary>
@@ -10,4 +15,10 @@ public sealed partial class WallMountDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public double Prob = 0.1;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
+
+    [DataField(required: true)]
+    public ProtoId<EntityTablePrototype> Contents;
 }
index b71e845a733706f45b99313d2081445b6a5555e4..a587d03a7c6fc505f441ec6da8c39b11326a3b4e 100644 (file)
@@ -1,3 +1,6 @@
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Procedural.PostGeneration;
 
 // Ime a worm
@@ -32,4 +35,7 @@ public sealed partial class WormCorridorDunGen : IDunGenLayer
     /// </summary>
     [DataField]
     public float Width = 3f;
+
+    [DataField(required: true)]
+    public ProtoId<ContentTileDefinition> Tile;
 }
index c0417224792136485456f70c7f3197d0df85e44b..3950b1b72bf123675655d893bbb7c23ffdb5ff8c 100644 (file)
@@ -50,12 +50,8 @@ public abstract partial class SharedSalvageSystem
                 var configProto =_proto.Index(configId);
                 var layers = new Dictionary<string, int>();
 
-                var data = new DungeonData();
-                data.Apply(configProto.Data);
-
                 var config = new DungeonConfig
                 {
-                    Data = data,
                     Layers = new(configProto.Layers),
                     MaxCount = configProto.MaxCount,
                     MaxOffset = configProto.MaxOffset,
index 480863692e3e185d44d4ccc1bc1dbe018268ad13..c00d9e100b45e092cc324e8d5f87164228412a6a 100644 (file)
@@ -76,6 +76,18 @@ public static class EntitySpawnCollection
         public float CumulativeProbability { get; set; } = 0f;
     }
 
+    public static List<string> GetSpawns(ProtoId<EntitySpawnEntryPrototype> proto, IPrototypeManager? protoManager = null, IRobustRandom? random = null)
+    {
+        IoCManager.Resolve(ref protoManager, ref random);
+        return GetSpawns(protoManager.Index(proto).Entries, random);
+    }
+
+    public static List<string?> GetSpawns(ProtoId<EntitySpawnEntryPrototype> proto, System.Random random, IPrototypeManager? protoManager = null)
+    {
+        IoCManager.Resolve(ref protoManager);
+        return GetSpawns(protoManager.Index(proto).Entries, random);
+    }
+
     /// <summary>
     ///     Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection.
     /// </summary>
index c296aafad6430cb864050d3e25fc7cb558a47db3..5da95929952f3ae246906e2a90393b0e349785c1 100644 (file)
@@ -1,8 +1,11 @@
-# Base configs
+# Setups
 - type: dungeonConfig
-  id: PlanetBase
+  id: Experiment
   layers:
   - !type:PrefabDunGen
+    roomWhitelist:
+      tags:
+      - SalvageExperiment
     presets:
     - Bucket
     - Wow
 
   - !type:CorridorDunGen
     width: 3
+    tile: FloorSteel
 
   - !type:DungeonEntranceDunGen
     count: 2
+    tile: FloorSteel
+    contents: BaseAirlock
 
   - !type:RoomEntranceDunGen
+    tile: FloorSteel
+    contents: BaseAirlock
 
   - !type:EntranceFlankDunGen
+    tile: FloorSteel
+    contents: BaseWindow
 
   - !type:ExternalWindowDunGen
+    tile: FloorSteel
+    contents: BaseWindow
 
   - !type:WallMountDunGen
+    contents: ScienceLabsWalls
+    tile: FloorSteel
 
   - !type:BoundaryWallDunGen
+    wall: WallSolid
+    cornerWall: WallReinforced
+    tile: FloorSteel
 
   - !type:JunctionDunGen
     width: 1
+    tile: FloorSteel
+    contents: BaseAirlock
 
   - !type:JunctionDunGen
+    width: 3
+    tile: FloorSteel
+    contents: BaseAirlock
 
   - !type:AutoCablingDunGen
+    entity: CableApcExtension
 
   - !type:CornerClutterDunGen
+    contents: BaseClutter
 
   - !type:CorridorDecalSkirtingDunGen
     cardinalDecals:
       NorthWest: BrickTileWhiteInnerNw
       NorthEast: BrickTileWhiteInnerNe
 
-# Setups
-- type: dungeonConfig
-  id: Experiment
-  data:
-    colors:
-      Decals: "#D381C996"
-    entities:
-      Cabling: CableApcExtension
-      CornerWalls: WallReinforced
-      Walls: WallSolid
-    spawnGroups:
-      CornerClutter: BaseClutter
-      Entrance: BaseAirlock
-      EntranceFlank: BaseWindow
-      Junction: BaseAirlock
-      WallMounts: ScienceLabsWalls
-      Window: BaseWindow
-    tiles:
-      FallbackTile: FloorSteel
-    whitelists:
-      Rooms:
-        tags:
-        - SalvageExperiment
-  layers:
-  - !type:PrototypeDunGen
-    proto: PlanetBase
-
 - type: dungeonConfig
   id: Haunted
-  data:
-    entities:
-      Walls: WallRock
-    tiles:
-      FallbackTile: FloorCaveDrought
-    whitelists:
-      Rooms:
-        tags:
-        - Mineshaft
   layers:
   - !type:PrefabDunGen
     presets:
 
   - !type:WormCorridorDunGen
     width: 3
+    tile: FloorCaveDrought
 
   - !type:CorridorClutterDunGen
-    contents:
-    - id: FloraStalagmite
+    contents: HauntedClutter
 
   - !type:BoundaryWallDunGen
+    tile: FloorCaveDrought
+    wall: WallRock
+
+- type: entityTable
+  id: HauntedClutter
+  table: !type:GroupSelector
+    children:
+    - id: FloraStalagmite
 
 - type: dungeonConfig
   id: LavaBrig
-  data:
-    colors:
-      Decals: "#DE3A3A96"
-    entities:
-      Cabling: CableApcExtension
-      CornerWalls: WallReinforced
-      Walls: WallSolid
-    spawnGroups:
-      CornerClutter: BaseClutter
-      Entrance: LavaBrigEntrance
-      EntranceFlank: BaseWindow
-      Junction: BaseAirlock
-      WallMounts: ScienceLabsWalls
-      Window: BaseWindow
-    tiles:
-      FallbackTile: FloorDark
-    whitelists:
-      Rooms:
-        tags:
-        - LavaBrig
   layers:
-  - !type:PrototypeDunGen
-    proto: PlanetBase
+  - !type:PrefabDunGen
+    roomWhitelist:
+      tags:
+      - LavaBrig
+    presets:
+    - Bucket
+    - Wow
+    - SpaceShip
+    - Tall
+
+  - !type:CorridorDunGen
+    width: 3
+    tile: FloorDark
+
+  - !type:DungeonEntranceDunGen
+    count: 2
+    tile: FloorDark
+    contents: LavaBrigEntrance
+
+  - !type:RoomEntranceDunGen
+    tile: FloorDark
+    contents: BaseAirlock
+
+  - !type:EntranceFlankDunGen
+    tile: FloorDark
+    contents: BaseWindow
+
+  - !type:ExternalWindowDunGen
+    tile: FloorDark
+    contents: BaseWindow
+
+  - !type:WallMountDunGen
+    contents: ScienceLabsWalls
+    tile: FloorDark
+
+  - !type:BoundaryWallDunGen
+    wall: WallSolid
+    cornerWall: WallReinforced
+    tile: FloorDark
+
+  - !type:JunctionDunGen
+    width: 1
+    tile: FloorDark
+    contents: BaseAirlock
+
+  - !type:JunctionDunGen
+    width: 3
+    tile: FloorDark
+    contents: BaseAirlock
+
+  - !type:AutoCablingDunGen
+    entity: CableApcExtension
+
+  - !type:CornerClutterDunGen
+    contents: BaseClutter
+
+  - !type:CorridorDecalSkirtingDunGen
+    cardinalDecals:
+      South: BrickTileWhiteLineS
+      East: BrickTileWhiteLineE
+      North: BrickTileWhiteLineN
+      West: BrickTileWhiteLineW
+    cornerDecals:
+      SouthEast: BrickTileWhiteCornerSe
+      SouthWest: BrickTileWhiteCornerSw
+      NorthEast: BrickTileWhiteCornerNe
+      NorthWest: BrickTileWhiteCornerNw
+    pocketDecals:
+      SouthWest: BrickTileWhiteInnerSw
+      SouthEast: BrickTileWhiteInnerSe
+      NorthWest: BrickTileWhiteInnerNw
+      NorthEast: BrickTileWhiteInnerNe
 
 - type: dungeonConfig
   id: Mineshaft
-  data:
-    entities:
-      Cabling: Catwalk
-    spawnGroups:
-      CornerClutter: MineshaftClutter
-      Entrance: BaseWoodWall
-      EntranceFlank: BaseWoodWall
-      Junction: BaseWoodSupport
-      Window: BaseWoodWall
-    tiles:
-      FallbackTile: FloorCaveDrought
-    whitelists:
-      Rooms:
-        tags:
-        - Mineshaft
   layers:
-  - !type:PrototypeDunGen
-    proto: PlanetBase
+  - !type:PrefabDunGen
+    roomWhitelist:
+      tags:
+      - Mineshaft
+    presets:
+    - Bucket
+    - Wow
+    - SpaceShip
+    - Tall
+
+  - !type:CorridorDunGen
+    width: 3
+    tile: FloorCaveDrought
+
+  - !type:DungeonEntranceDunGen
+    count: 2
+    tile: FloorCaveDrought
+    contents: BaseWoodWall
+
+  - !type:RoomEntranceDunGen
+    tile: FloorCaveDrought
+    contents: BaseWoodWall
+
+  - !type:EntranceFlankDunGen
+    tile: FloorCaveDrought
+    contents: BaseWoodWall
+
+  - !type:ExternalWindowDunGen
+    tile: FloorCaveDrought
+    contents: BaseWoodWall
+
+  # No wallmounts
+
+  # No boundary wall
+
+  - !type:JunctionDunGen
+    width: 1
+    tile: FloorCaveDrought
+    contents: BaseWoodSupport
+
+  - !type:JunctionDunGen
+    width: 3
+    tile: FloorCaveDrought
+    contents: BaseWoodSupport
+
+  - !type:AutoCablingDunGen
+    entity: Catwalk
+
+  - !type:CornerClutterDunGen
+    contents: MineshaftClutter
+
+# TODO: Check decal skirting fix contents in code.
 
 - type: dungeonConfig
   id: SnowyLabs
-  data:
-    colors:
-      Decals: "#4cc7aa96"
-    entities:
-      Cabling: CableApcExtension
-      CornerWalls: WallSilver
-      Walls: WallSilver
-    spawnGroups:
-      CornerClutter: BaseClutter
-      Entrance: SnowyLabsEntrance
-      EntranceFlank: BaseWindow
-      Junction: BaseAirlock
-      WallMounts: SnowyLabsWalls
-      Window: BaseWindow
-    tiles:
-      FallbackTile: FloorSteel
-    whitelists:
-      Rooms:
-        tags:
-        - SnowyLabs
   layers:
-  - !type:PrototypeDunGen
-    proto: PlanetBase
+  - !type:PrefabDunGen
+    roomWhitelist:
+      tags:
+      - SnowyLabs
+    presets:
+    - Bucket
+    - Wow
+    - SpaceShip
+    - Tall
+
+  - !type:CorridorDunGen
+    width: 3
+    tile: FloorSteel
+
+  - !type:DungeonEntranceDunGen
+    count: 2
+    tile: FloorSteel
+    contents: BaseAirlock
+
+  - !type:RoomEntranceDunGen
+    tile: FloorSteel
+    contents: BaseAirlock
+
+  - !type:EntranceFlankDunGen
+    tile: FloorSteel
+    contents: BaseWindow
+
+  - !type:ExternalWindowDunGen
+    tile: FloorSteel
+    contents: BaseWindow
+
+  - !type:WallMountDunGen
+    contents: SnowyLabsWalls
+    tile: FloorSteel
+
+  - !type:BoundaryWallDunGen
+    wall: WallSilver
+    tile: FloorSteel
+
+  - !type:JunctionDunGen
+    width: 1
+    tile: FloorSteel
+    contents: BaseAirlock
+
+  - !type:JunctionDunGen
+    width: 3
+    tile: FloorSteel
+    contents: BaseAirlock
+
+  - !type:AutoCablingDunGen
+    entity: CableApcExtension
 
 # Spawn groups
 # Basic
-- type: entitySpawnEntry
+- type: entityTable
   id: BaseClutter
-  entries:
-  - id: PottedPlantRandom
-    amount: 1
+  table: !type:GroupSelector
+    children:
+    - id: PottedPlantRandom
+      amount: 1
 
-- type: entitySpawnEntry
+- type: entityTable
   id: BaseAirlock
-  entries:
-  - id: CableApcExtension
-  - id: AirlockGlass
+  table: !type:GroupSelector
+    children:
+    - id: CableApcExtension
+    - id: AirlockGlass
 
-- type: entitySpawnEntry
+- type: entityTable
   id: BaseWindow
-  entries:
-  - id: Grille
-  - id: Window
+  table: !type:GroupSelector
+    children:
+    - id: Grille
+    - id: Window
 
 # Lava brig
-- type: entitySpawnEntry
+- type: entityTable
   id: LavaBrigEntrance
-  entries:
-  - id: CableApcExtension
-  - id: AirlockSecurityGlassLocked
+  table: !type:GroupSelector
+    children:
+    - id: CableApcExtension
+    - id: AirlockSecurityGlassLocked
 
 # Mineshaft
-- type: entitySpawnEntry
+- type: entityTable
   id: BaseWoodWall
-  entries:
-  - id: RandomWoodenWall
+  table: !type:GroupSelector
+    children:
+    - id: RandomWoodenWall
 
-- type: entitySpawnEntry
+- type: entityTable
   id: BaseWoodSupport
-  entries:
-  - id: RandomWoodenSupport
+  table: !type:GroupSelector
+    children:
+    - id: RandomWoodenSupport
 
-- type: entitySpawnEntry
+- type: entityTable
   id: MineshaftClutter
-  entries:
-  - id: RandomStalagmiteOrCrystal
-    amount: 1
+  table: !type:GroupSelector
+    children:
+    - id: RandomStalagmiteOrCrystal
+      amount: 1
 
-- type: entitySpawnEntry
+- type: entityTable
   id: MineshaftWalls
-  entries:
-  # Ore
-  - id: WallRockSalt
-    prob: 0.6
-    orGroup: content
-  - id: WallRockCoal
-    prob: 0.6
-    orGroup: content
-  - id: WallRockTin
-    prob: 0.4
-    orGroup: content
-  - id: WallMining
-    prob: 0.8
-    orGroup: content
+  table: !type:GroupSelector
+    children:
+    # Ore
+    - id: WallRockSalt
+      prob: 0.6
+    - id: WallRockCoal
+      prob: 0.6
+    - id: WallRockTin
+      prob: 0.4
+    - id: WallMining
+      prob: 0.8
 
 # Science lab
-- type: entitySpawnEntry
+- type: entityTable
   id: ScienceLabsWalls
-  entries:
-  # Posters
-  - id: RandomPosterLegit
-    orGroup: content
-  - id: ExtinguisherCabinetFilled
-    prob: 0.2
-    orGroup: content
-  - id: RandomPainting
-    prob: 0.05
-    orGroup: content
-  - id: IntercomCommon
-    prob: 0.1
-    orGroup: content
+  table: !type:GroupSelector
+    children:
+    # Posters
+    - id: RandomPosterLegit
+    - id: ExtinguisherCabinetFilled
+      prob: 0.2
+    - id: RandomPainting
+      prob: 0.05
+    - id: IntercomCommon
+      prob: 0.1
 
 # Snowy labs
-- type: entitySpawnEntry
+- type: entityTable
   id: SnowyLabsEntrance
-  entries:
-  - id: CableApcExtension
-  - id: AirlockFreezerHydroponicsLocked
+  table: !type:GroupSelector
+    children:
+    - id: CableApcExtension
+    - id: AirlockFreezerHydroponicsLocked
 
-- type: entitySpawnEntry
+- type: entityTable
   id: SnowyLabsWalls
-  entries:
-  # Posters
-  - id: RandomPosterLegit
-    orGroup: content
-  - id: ExtinguisherCabinetFilled
-    prob: 0.2
-    orGroup: content
-  - id: RandomPainting
-    prob: 0.05
-    orGroup: content
-  - id: IntercomScience
-    prob: 0.1
-    orGroup: content
+  table: !type:GroupSelector
+    children:
+    # Posters
+    - id: RandomPosterLegit
+    - id: ExtinguisherCabinetFilled
+      prob: 0.2
+    - id: RandomPainting
+      prob: 0.05
+    - id: IntercomScience
+      prob: 0.1
index e9044720cc12054e33e7754260ccf248be93977a..7547072c4d131ca851bc7ee6e2038c2c9c983322 100644 (file)
@@ -18,6 +18,7 @@
       proto: VGRoidSmaller
     - !type:PrototypeDunGen
       proto: VGRoidSmallPaths
+      inheritDungeons: All
     - !type:EntityTableDunGen
       minCount: 7
       maxCount: 12
@@ -26,6 +27,7 @@
     # Fill
     - !type:PrototypeDunGen
       proto: VGRoidFill
+      inheritDungeons: All
     # Ores
     - !type:OreDunGen
       replacement: IronRock
 - type: dungeonConfig
   id: VGRoidExterior
   reserveTiles: true
-  data:
-    tiles:
-      FallbackTile: PlatingAsteroid
-      WidenTile: FloorAsteroidSand
   layers:
   - !type:PrototypeDunGen
     proto: VGRoidExteriorDungeons
   - !type:SplineDungeonConnectorDunGen
+    tile: PlatingAsteroid
+    widenTile: FloorAsteroidSand
 
 - type: dungeonConfig
   id: VGRoidExteriorDungeons
   - !type:MobsDunGen
     minCount: 8
     maxCount: 15
-    groups:
+    contents: VGRoidExteriorDungeonsMobs
+
+- type: entityTable
+  id: VGRoidExteriorDungeonsMobs
+  table: !type:GroupSelector
+    children:
     - id: SalvageSpawnerMobMiningAsteroid
       amount: 1
 
   - !type:MobsDunGen
     minCount: 25
     maxCount: 35
-    groups:
+    contents: VGRoidSmallPathsMobs
+
+- type: entityTable
+  id: VGRoidSmallPathsMobs
+  table: !type:GroupSelector
+    children:
     - id: SalvageSpawnerMobMiningAsteroid
       amount: 1
 
 # Fill with rocks.
 - type: dungeonConfig
   id: VGRoidFill
-  data:
-    entities:
-      Fill: IronRock
   layers:
   - !type:FillGridDunGen
+    entity: IronRock
     allowedTiles:
     - FloorAsteroidSand