DungeonData rework (#37172)

* DungeonData rework

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

* wawawewa

* Fix this

* Fixes

* review

* thanks fork

* fix
This commit is contained in:
metalgearsloth
2025-05-18 03:10:30 +10:00
committed by GitHub
parent d72939ba4b
commit 4afccdd5db
57 changed files with 759 additions and 696 deletions

View File

@@ -13,14 +13,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="AutoCablingDunGen"/> /// <see cref="AutoCablingDunGen"/>
/// </summary> /// </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. // There's a lot of ways you could do this.
// For now we'll just connect every LV cable in the dungeon. // For now we'll just connect every LV cable in the dungeon.
var cableTiles = new HashSet<Vector2i>(); var cableTiles = new HashSet<Vector2i>();
@@ -157,7 +151,7 @@ public sealed partial class DungeonJob
if (found) if (found)
continue; continue;
_entManager.SpawnEntity(ent, _maps.GridTileToLocal(_gridUid, _grid, tile)); _entManager.SpawnEntity(gen.Entity, _maps.GridTileToLocal(_gridUid, _grid, tile));
} }
} }
} }

View File

@@ -14,7 +14,7 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="BiomeDunGen"/> /// <see cref="BiomeDunGen"/>
/// </summary> /// </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)) if (!_prototype.TryIndex(dunGen.BiomeTemplate, out var indexedBiome))
return; return;

View File

@@ -15,7 +15,7 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="BiomeMarkerLayerDunGen"/> /// <see cref="BiomeMarkerLayerDunGen"/>
/// </summary> /// </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 we're adding biome then disable it and just use for markers.
if (_entManager.EnsureComponent(_gridUid, out BiomeComponent biomeComp)) if (_entManager.EnsureComponent(_gridUid, out BiomeComponent biomeComp))

View File

@@ -12,27 +12,13 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="BoundaryWallDunGen"/> /// <see cref="BoundaryWallDunGen"/>
/// </summary> /// </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) || var tileDef = _tileDefManager[gen.Tile];
!data.Entities.TryGetValue(DungeonDataKey.Walls, out var wall))
{
_sawmill.Error($"Error finding dungeon data for {nameof(gen)}");
return;
}
var tileDef = _tileDefManager[protoTileDef];
var tiles = new List<(Vector2i Index, Tile Tile)>(dungeon.RoomExteriorTiles.Count); var tiles = new List<(Vector2i Index, Tile Tile)>(dungeon.RoomExteriorTiles.Count);
if (!data.Entities.TryGetValue(DungeonDataKey.CornerWalls, out var cornerWall)) var wall = gen.Wall;
{ var cornerWall = gen.CornerWall ?? gen.Wall;
cornerWall = wall;
}
if (cornerWall == default)
{
cornerWall = wall;
}
// Spawn wall outline // Spawn wall outline
// - Tiles first // - Tiles first

View File

@@ -1,8 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Procedural.PostGeneration; using Content.Shared.Procedural.PostGeneration;
using Content.Shared.Storage;
using Robust.Shared.Physics.Components;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Procedural.DungeonJob; namespace Content.Server.Procedural.DungeonJob;
@@ -12,16 +10,15 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="CornerClutterDunGen"/> /// <see cref="CornerClutterDunGen"/>
/// </summary> /// </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)) var contentsTable = _prototype.Index(gen.Contents);
{
_sawmill.Error(Environment.StackTrace);
return;
}
foreach (var tile in dungeon.CorridorTiles) foreach (var tile in dungeon.CorridorTiles)
{ {
if (reservedTiles.Contains(tile))
continue;
var blocked = _anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask); var blocked = _anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask);
if (blocked) if (blocked)
@@ -45,8 +42,8 @@ public sealed partial class DungeonJob
if (random.Prob(gen.Chance)) if (random.Prob(gen.Chance))
{ {
var coords = _maps.GridTileToLocal(_gridUid, _grid, tile); var coords = _maps.GridTileToLocal(_gridUid, _grid, tile);
var protos = EntitySpawnCollection.GetSpawns(_prototype.Index(corner).Entries, random); var protos = contentsTable.Table.GetSpawns(random, _entManager, _prototype);
_entManager.SpawnEntities(coords, protos); _entManager.SpawnEntitiesAttachedTo(coords, protos);
} }
break; break;

View File

@@ -12,14 +12,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="CorridorDunGen"/> /// <see cref="CorridorDunGen"/>
/// </summary> /// </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); var entrances = new List<Vector2i>(dungeon.Rooms.Count);
// Grab entrances // Grab entrances
@@ -98,7 +92,7 @@ public sealed partial class DungeonJob
WidenCorridor(dungeon, gen.Width, corridorTiles); WidenCorridor(dungeon, gen.Width, corridorTiles);
var setTiles = new List<(Vector2i, Tile)>(); var setTiles = new List<(Vector2i, Tile)>();
var tileDef = (ContentTileDefinition) _tileDefManager[tileProto]; var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
foreach (var tile in corridorTiles) foreach (var tile in corridorTiles)
{ {

View File

@@ -12,10 +12,11 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="CorridorClutterDunGen"/> /// <see cref="CorridorClutterDunGen"/>
/// </summary> /// </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 physicsQuery = _entManager.GetEntityQuery<PhysicsComponent>();
var count = (int) Math.Ceiling(dungeon.CorridorTiles.Count * gen.Chance); var count = (int) Math.Ceiling(dungeon.CorridorTiles.Count * gen.Chance);
var contents = _prototype.Index(gen.Contents);
while (count > 0) while (count > 0)
{ {
@@ -42,9 +43,12 @@ public sealed partial class DungeonJob
count--; 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); var coords = _maps.ToCenterCoordinates(_gridUid, tile, _grid);
_entManager.SpawnEntities(coords, protos); _entManager.SpawnEntitiesAttachedTo(coords, protos);
await SuspendIfOutOfTime(); await SuspendIfOutOfTime();
if (!ValidateResume()) if (!ValidateResume())

View File

@@ -13,13 +13,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="CorridorDecalSkirtingDunGen"/> /// <see cref="CorridorDecalSkirtingDunGen"/>
/// </summary> /// </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 directions = new ValueList<DirectionFlag>(4);
var pocketDirections = new ValueList<Direction>(4); var pocketDirections = new ValueList<Direction>(4);
var doorQuery = _entManager.GetEntityQuery<DoorComponent>(); var doorQuery = _entManager.GetEntityQuery<DoorComponent>();
@@ -88,7 +83,7 @@ public sealed partial class DungeonJob
{ {
// Decals not being centered biting my ass again // Decals not being centered biting my ass again
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); 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 // Decals not being centered biting my ass again
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); 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; continue;
@@ -116,7 +111,7 @@ public sealed partial class DungeonJob
if (decks.CornerDecals.TryGetValue(dirFlag, out var cDir)) if (decks.CornerDecals.TryGetValue(dirFlag, out var cDir))
{ {
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset);
_decals.TryAddDecal(cDir, gridPos, out _, color: color); _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color);
} }
} }
} }

View File

@@ -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;
}
}

View File

@@ -14,15 +14,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="PrefabDunGen"/> /// <see cref="PrefabDunGen"/>
/// </summary> /// </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 preset = prefab.Presets[random.Next(prefab.Presets.Count)];
var gen = _prototype.Index(preset); var gen = _prototype.Index(preset);
@@ -50,9 +43,9 @@ public sealed partial class DungeonJob
{ {
var whitelisted = false; 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)) if (proto.Tags.Contains(tag))
{ {
@@ -164,6 +157,7 @@ public sealed partial class DungeonJob
// Then for overlaps choose either 1x1 / 3x1 // Then for overlaps choose either 1x1 / 3x1
// Pick a random tile for it and then expand outwards as relevant (weighted towards middle?) // 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++) for (var i = 0; i < chosenPacks.Length; i++)
{ {
@@ -181,29 +175,35 @@ public sealed partial class DungeonJob
Angle roomRotation = Angle.Zero; Angle roomRotation = Angle.Zero;
Matrix3x2 matty; Matrix3x2 matty;
// If no room found then try rotated dimensions
if (!roomProtos.TryGetValue(roomDimensions, out var roomProto)) if (!roomProtos.TryGetValue(roomDimensions, out var roomProto))
{ {
roomDimensions = new Vector2i(roomDimensions.Y, roomDimensions.X); 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)) if (!roomProtos.TryGetValue(roomDimensions, out roomProto))
{ {
matty = Matrix3x2.Multiply(packTransform, dungeonTransform); if (fallbackTile != null)
for (var x = roomSize.Left; x < roomSize.Right; x++)
{ {
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)) if (reservedTiles.Contains(index))
continue; 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."); _sawmill.Error($"Unable to find room variant for {roomDimensions}, leaving empty.");
continue; continue;
} }

View File

@@ -12,49 +12,43 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="ReplaceTileDunGen"/> /// <see cref="ReplaceTileDunGen"/>
/// </summary> /// </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 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;
foreach (var layer in gen.Layers)
{ {
var value = layer.Noise.GetNoise(node.X, node.Y); if (reservedTiles.Contains(node))
if (value < layer.Threshold)
continue; continue;
Tile tile; foreach (var layer in gen.Layers)
{
var value = layer.Noise.GetNoise(node.X, node.Y);
if (random.Prob(gen.VariantWeight)) if (value < layer.Threshold)
{ continue;
tile = _tileDefManager.GetVariantTile(_prototype.Index(layer.Tile), random);
} Tile tile;
else
{ if (random.Prob(gen.VariantWeight))
tile = new Tile(_prototype.Index(layer.Tile).TileId); {
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)); await SuspendDungeon();
reserved.Add(node);
break;
} }
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>()),
});
} }
} }

View File

@@ -12,18 +12,12 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="DungeonEntranceDunGen"/> /// <see cref="DungeonEntranceDunGen"/>
/// </summary> /// </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 rooms = new List<DungeonRoom>(dungeon.Rooms);
var roomTiles = new List<Vector2i>(); 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++) for (var i = 0; i < gen.Count; i++)
{ {
@@ -82,7 +76,7 @@ public sealed partial class DungeonJob
var gridCoords = _maps.GridTileToLocal(_gridUid, _grid, tile); var gridCoords = _maps.GridTileToLocal(_gridUid, _grid, tile);
// Need to offset the spawn to avoid spawning in the room. // 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); _entManager.SpawnAtPosition(ent, gridCoords);
} }

View File

@@ -13,18 +13,12 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="EntranceFlankDunGen"/> /// <see cref="EntranceFlankDunGen"/>
/// </summary> /// </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 tiles = new List<(Vector2i Index, Tile)>();
var tileDef = _tileDefManager[tileProto]; var tileDef = _tileDefManager[gen.Tile];
var spawnPositions = new ValueList<Vector2i>(dungeon.Rooms.Count); var spawnPositions = new ValueList<Vector2i>(dungeon.Rooms.Count);
var contents = _prototype.Index(gen.Contents);
foreach (var room in dungeon.Rooms) foreach (var room in dungeon.Rooms)
{ {
@@ -48,11 +42,10 @@ public sealed partial class DungeonJob
} }
_maps.SetTiles(_gridUid, _grid, tiles); _maps.SetTiles(_gridUid, _grid, tiles);
var entGroup = _prototype.Index(flankProto);
foreach (var entrance in spawnPositions) 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));
} }
} }
} }

View File

@@ -1,10 +1,8 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.NPC.Pathfinding;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.NPC; using Content.Shared.NPC;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Procedural.DungeonGenerators; using Content.Shared.Procedural.DungeonGenerators;
using Robust.Shared.Collections;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -27,7 +25,6 @@ public sealed partial class DungeonJob
var startTile = new Vector2i(0, (int) distance).Rotate(angle); var startTile = new Vector2i(0, (int) distance).Rotate(angle);
Vector2i? dungeonSpawn = null; Vector2i? dungeonSpawn = null;
var pathfinder = _entManager.System<PathfindingSystem>();
// Gridcast // Gridcast
SharedPathfindingSystem.GridCast(startTile, position, tile => SharedPathfindingSystem.GridCast(startTile, position, tile =>
@@ -52,7 +49,7 @@ public sealed partial class DungeonJob
var config = _prototype.Index(dungen.Proto); var config = _prototype.Index(dungen.Proto);
var nextSeed = random.Next(); 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; return dungeons;
} }

View File

@@ -24,15 +24,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="ExternalWindowDunGen"/> /// <see cref="ExternalWindowDunGen"/>
/// </summary> /// </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. // Iterate every tile with N chance to spawn windows on that wall per cardinal dir.
var chance = 0.25 / 3f; var chance = 0.25 / 3f;
@@ -42,7 +35,7 @@ public sealed partial class DungeonJob
random.Shuffle(validTiles); random.Shuffle(validTiles);
var tiles = new List<(Vector2i, Tile)>(); var tiles = new List<(Vector2i, Tile)>();
var tileDef = _tileDefManager[tileProto]; var tileDef = _tileDefManager[gen.Tile];
var count = Math.Floor(validTiles.Count * chance); var count = Math.Floor(validTiles.Count * chance);
var index = 0; var index = 0;
var takenTiles = new HashSet<Vector2i>(); var takenTiles = new HashSet<Vector2i>();
@@ -120,15 +113,13 @@ public sealed partial class DungeonJob
} }
_maps.SetTiles(_gridUid, _grid, tiles); _maps.SetTiles(_gridUid, _grid, tiles);
index = 0; var contents = _prototype.Index(gen.Contents);
var spawnEntry = _prototype.Index(windowGroup);
foreach (var tile in tiles) foreach (var tile in tiles)
{ {
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile.Item1); var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile.Item1);
index += spawnEntry.Entries.Count; _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random));
_entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(spawnEntry.Entries, random));
await SuspendDungeon(); await SuspendDungeon();
if (!ValidateResume()) if (!ValidateResume())

View File

@@ -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;
}
}
}
}

View File

@@ -10,9 +10,7 @@ namespace Content.Server.Procedural.DungeonJob;
public sealed partial class DungeonJob public sealed partial class DungeonJob
{ {
/* // Various helper methods.
* Run after the main dungeon generation
*/
private static readonly ProtoId<TagPrototype> WallTag = "Wall"; private static readonly ProtoId<TagPrototype> WallTag = "Wall";

View File

@@ -12,21 +12,14 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="InternalWindowDunGen"/> /// <see cref="InternalWindowDunGen"/>
/// </summary> /// </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 // 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 // If so then consider windows
var minDistance = 4; var minDistance = 4;
var maxDistance = 6; var maxDistance = 6;
var tileDef = _tileDefManager[tileProto]; var tileDef = _tileDefManager[gen.Tile];
var window = _prototype.Index(windowGroup); var contents = _prototype.Index(gen.Contents);
foreach (var room in dungeon.Rooms) foreach (var room in dungeon.Rooms)
{ {
@@ -90,7 +83,7 @@ public sealed partial class DungeonJob
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile); var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile);
_maps.SetTile(_gridUid, _grid, tile, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)); _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) if (validTiles.Count > 0)

View File

@@ -12,17 +12,10 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="JunctionDunGen"/> /// <see cref="JunctionDunGen"/>
/// </summary> /// </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) || var tileDef = _tileDefManager[gen.Tile];
!data.SpawnGroups.TryGetValue(DungeonDataKey.Junction, out var junctionProto)) var contents = _prototype.Index(gen.Contents);
{
_sawmill.Error($"Dungeon data keys are missing for {nameof(gen)}");
return;
}
var tileDef = _tileDefManager[tileProto];
var entranceGroup = _prototype.Index(junctionProto);
// N-wide junctions // N-wide junctions
foreach (var tile in dungeon.CorridorTiles) 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)); _maps.SetTile(_gridUid, _grid, weh, _tile.GetVariantTile((ContentTileDefinition) tileDef, random));
var coords = _maps.GridTileToLocal(_gridUid, _grid, weh); var coords = _maps.GridTileToLocal(_gridUid, _grid, weh);
_entManager.SpawnEntities(coords, EntitySpawnCollection.GetSpawns(entranceGroup.Entries, random)); _entManager.SpawnEntitiesAttachedTo(coords, _entTable.GetSpawns(contents, random));
} }
break; break;

View File

@@ -13,19 +13,8 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="MiddleConnectionDunGen"/> /// <see cref="MiddleConnectionDunGen"/>
/// </summary> /// </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 // Grab all of the room bounds
// Then, work out connections between them // Then, work out connections between them
var roomBorders = new Dictionary<DungeonRoom, HashSet<Vector2i>>(dungeon.Rooms.Count); var roomBorders = new Dictionary<DungeonRoom, HashSet<Vector2i>>(dungeon.Rooms.Count);
@@ -67,7 +56,9 @@ public sealed partial class DungeonJob
// TODO: Optional loops // TODO: Optional loops
var roomConnections = new Dictionary<DungeonRoom, List<DungeonRoom>>(); 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) foreach (var (room, border) in roomBorders)
{ {
@@ -118,16 +109,16 @@ public sealed partial class DungeonJob
width--; width--;
_maps.SetTile(_gridUid, _grid, node, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)); _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 else
{ {
// Iterate neighbors and check for blockers, if so bulldoze // Iterate neighbors and check for blockers, if so bulldoze
ClearDoor(dungeon, _grid, node); ClearDoor(dungeon, _grid, node);
_entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(entrance.Entries, random)); _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random));
} }
if (width == 0) if (width == 0)

View File

@@ -13,6 +13,9 @@ namespace Content.Server.Procedural.DungeonJob;
public sealed partial class DungeonJob public sealed partial class DungeonJob
{ {
/// <summary>
/// <see cref="MobsDunGen"/>
/// </summary>
private async Task PostGen( private async Task PostGen(
MobsDunGen gen, MobsDunGen gen,
Dungeon dungeon, Dungeon dungeon,
@@ -21,8 +24,8 @@ public sealed partial class DungeonJob
var availableRooms = new ValueList<DungeonRoom>(); var availableRooms = new ValueList<DungeonRoom>();
availableRooms.AddRange(dungeon.Rooms); availableRooms.AddRange(dungeon.Rooms);
var availableTiles = new ValueList<Vector2i>(dungeon.AllTiles); 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 count = random.Next(gen.MinCount, gen.MaxCount + 1);
var npcs = _entManager.System<NPCSystem>(); var npcs = _entManager.System<NPCSystem>();
@@ -38,6 +41,8 @@ public sealed partial class DungeonJob
continue; continue;
} }
var entities = _entTable.GetSpawns(contents, random);
foreach (var ent in entities) foreach (var ent in entities)
{ {
var uid = _entManager.SpawnAtPosition(ent, _maps.GridTileToLocal(_gridUid, _grid, tile)); var uid = _entManager.SpawnAtPosition(ent, _maps.GridTileToLocal(_gridUid, _grid, tile));

View File

@@ -1,6 +0,0 @@
namespace Content.Server.Procedural.DungeonJob;
public sealed partial class DungeonJob
{
}

View File

@@ -12,23 +12,19 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="RoomEntranceDunGen"/> /// <see cref="RoomEntranceDunGen"/>
/// </summary> /// </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 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 room in dungeon.Rooms)
{ {
foreach (var entrance in room.Entrances) foreach (var entrance in room.Entrances)
{ {
if (reservedTiles.Contains(entrance))
continue;
setTiles.Add((entrance, _tile.GetVariantTile((ContentTileDefinition) tileDef, random))); setTiles.Add((entrance, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)));
} }
} }
@@ -39,9 +35,17 @@ public sealed partial class DungeonJob
{ {
foreach (var entrance in room.Entrances) foreach (var entrance in room.Entrances)
{ {
_entManager.SpawnEntities( if (reservedTiles.Contains(entrance))
continue;
_entManager.SpawnEntitiesAttachedTo(
_maps.GridTileToLocal(_gridUid, _grid, entrance), _maps.GridTileToLocal(_gridUid, _grid, entrance),
EntitySpawnCollection.GetSpawns(entranceIn.Entries, random)); _entTable.GetSpawns(contents, random));
await SuspendDungeon();
if (!ValidateResume())
return;
} }
} }
} }

View File

@@ -15,25 +15,14 @@ public sealed partial class DungeonJob
/// </summary> /// </summary>
private async Task<Dungeon> PostGen( private async Task<Dungeon> PostGen(
SplineDungeonConnectorDunGen gen, SplineDungeonConnectorDunGen gen,
DungeonData data,
List<Dungeon> dungeons, List<Dungeon> dungeons,
HashSet<Vector2i> reservedTiles, HashSet<Vector2i> reservedTiles,
Random random) Random random)
{ {
// TODO: The path itself use the tile
// Widen it randomly (probably for each tile offset it by some changing amount).
// NOOP // NOOP
if (dungeons.Count <= 1) if (dungeons.Count <= 1)
return Dungeon.Empty; 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>(); var nodes = new List<Vector2i>();
foreach (var dungeon in dungeons) foreach (var dungeon in dungeons)
@@ -57,7 +46,8 @@ public sealed partial class DungeonJob
var tiles = new List<(Vector2i Index, Tile Tile)>(); var tiles = new List<(Vector2i Index, Tile Tile)>();
var pathfinding = _entManager.System<PathfindingSystem>(); var pathfinding = _entManager.System<PathfindingSystem>();
var allTiles = new HashSet<Vector2i>(); 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) foreach (var pair in tree)
{ {
@@ -112,7 +102,7 @@ public sealed partial class DungeonJob
if (random.Prob(0.9f)) if (random.Prob(0.9f))
{ {
tile = new Tile(_prototype.Index(widen).TileId); tile = new Tile(widen.TileId);
} }
else else
{ {
@@ -132,7 +122,7 @@ public sealed partial class DungeonJob
continue; continue;
allTiles.Add(node); allTiles.Add(node);
tiles.Add((node, fallbackTile)); tiles.Add((node, pathTile));
} }
_maps.SetTiles(_gridUid, _grid, tiles); _maps.SetTiles(_gridUid, _grid, tiles);

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Shared.Maps;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Procedural.PostGeneration; using Content.Shared.Procedural.PostGeneration;
using Content.Shared.Storage; using Content.Shared.Storage;
@@ -11,25 +12,13 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="WallMountDunGen"/> /// <see cref="WallMountDunGen"/>
/// </summary> /// </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 checkedTiles = new HashSet<Vector2i>();
var allExterior = new HashSet<Vector2i>(dungeon.CorridorExteriorTiles); var allExterior = new HashSet<Vector2i>(dungeon.CorridorExteriorTiles);
allExterior.UnionWith(dungeon.RoomExteriorTiles); allExterior.UnionWith(dungeon.RoomExteriorTiles);
var count = 0; var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
var contents = _prototype.Index(gen.Contents);
foreach (var neighbor in allExterior) foreach (var neighbor in allExterior)
{ {
@@ -40,21 +29,18 @@ public sealed partial class DungeonJob
if (!random.Prob(gen.Prob) || !checkedTiles.Add(neighbor)) if (!random.Prob(gen.Prob) || !checkedTiles.Add(neighbor))
continue; continue;
if (reservedTiles.Contains(neighbor))
continue;
_maps.SetTile(_gridUid, _grid, neighbor, _tile.GetVariantTile(tileDef, random)); _maps.SetTile(_gridUid, _grid, neighbor, _tile.GetVariantTile(tileDef, random));
var gridPos = _maps.GridTileToLocal(_gridUid, _grid, neighbor); var gridPos = _maps.GridTileToLocal(_gridUid, _grid, neighbor);
var protoNames = EntitySpawnCollection.GetSpawns(_prototype.Index(spawnProto).Entries, random); var protoNames = _entTable.GetSpawns(contents, random);
_entManager.SpawnEntities(gridPos, protoNames); _entManager.SpawnEntitiesAttachedTo(gridPos, protoNames);
count += protoNames.Count;
if (count > 20) await SuspendDungeon();
{ if (!ValidateResume())
count -= 20; return;
await SuspendDungeon();
if (!ValidateResume())
return;
}
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Shared.Maps;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Procedural.PostGeneration; using Content.Shared.Procedural.PostGeneration;
using Robust.Shared.Collections; using Robust.Shared.Collections;
@@ -14,19 +15,14 @@ public sealed partial class DungeonJob
/// <summary> /// <summary>
/// <see cref="WormCorridorDunGen"/> /// <see cref="WormCorridorDunGen"/>
/// </summary> /// </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)>(); var networks = new List<(Vector2i Start, HashSet<Vector2i> Network)>();
// List of places to start from. // List of places to start from.
var worm = new ValueList<Vector2i>(); var worm = new ValueList<Vector2i>();
var startAngles = new Dictionary<Vector2i, Angle>(); var startAngles = new Dictionary<Vector2i, Angle>();
var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile];
foreach (var room in dungeon.Rooms) foreach (var room in dungeon.Rooms)
{ {

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Decals; using Content.Server.Decals;
@@ -6,6 +7,7 @@ using Content.Server.NPC.HTN;
using Content.Server.NPC.Systems; using Content.Server.NPC.Systems;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.EntitySystems;
using Content.Shared.EntityTable;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Procedural.DungeonGenerators; using Content.Shared.Procedural.DungeonGenerators;
@@ -37,6 +39,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
private readonly DecalSystem _decals; private readonly DecalSystem _decals;
private readonly DungeonSystem _dungeon; private readonly DungeonSystem _dungeon;
private readonly EntityLookupSystem _lookup; private readonly EntityLookupSystem _lookup;
private readonly EntityTableSystem _entTable;
private readonly TagSystem _tags; private readonly TagSystem _tags;
private readonly TileSystem _tile; private readonly TileSystem _tile;
private readonly SharedMapSystem _maps; private readonly SharedMapSystem _maps;
@@ -88,6 +91,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
_tile = tile; _tile = tile;
_tags = _entManager.System<TagSystem>(); _tags = _entManager.System<TagSystem>();
_maps = _entManager.System<SharedMapSystem>(); _maps = _entManager.System<SharedMapSystem>();
_entTable = _entManager.System<EntityTableSystem>();
_transform = transform; _transform = transform;
_physicsQuery = _entManager.GetEntityQuery<PhysicsComponent>(); _physicsQuery = _entManager.GetEntityQuery<PhysicsComponent>();
@@ -108,13 +112,20 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
private async Task<List<Dungeon>> GetDungeons( private async Task<List<Dungeon>> GetDungeons(
Vector2i position, Vector2i position,
DungeonConfig config, DungeonConfig config,
DungeonData data,
List<IDunGenLayer> layers, List<IDunGenLayer> layers,
HashSet<Vector2i> reservedTiles, HashSet<Vector2i> reservedTiles,
int seed, int seed,
Random random) Random random,
List<Dungeon>? existing = null)
{ {
var dungeons = new List<Dungeon>(); 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); var count = random.Next(config.MinCount, config.MaxCount + 1);
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
@@ -123,10 +134,25 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
foreach (var layer in layers) 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) 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) foreach (var dungeon in dungeons)
{ {
reservedTiles.UnionWith(dungeon.AllTiles); 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. // Tiles we can no longer generate on due to being reserved elsewhere.
var reservedTiles = new HashSet<Vector2i>(); 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. // To make it slightly more deterministic treat this RNG as separate ig.
// Post-processing after finishing loading. // Post-processing after finishing loading.
@@ -181,7 +207,6 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
private async Task RunLayer( private async Task RunLayer(
List<Dungeon> dungeons, List<Dungeon> dungeons,
DungeonData data,
Vector2i position, Vector2i position,
IDunGenLayer layer, IDunGenLayer layer,
HashSet<Vector2i> reservedTiles, HashSet<Vector2i> reservedTiles,
@@ -200,52 +225,52 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
switch (layer) switch (layer)
{ {
case AutoCablingDunGen cabling: case AutoCablingDunGen cabling:
await PostGen(cabling, data, dungeons[^1], reservedTiles, random); await PostGen(cabling, dungeons[^1], reservedTiles, random);
break; break;
case BiomeMarkerLayerDunGen markerPost: case BiomeMarkerLayerDunGen markerPost:
await PostGen(markerPost, data, dungeons[^1], reservedTiles, random); await PostGen(markerPost, dungeons[^1], reservedTiles, random);
break; break;
case BiomeDunGen biome: case BiomeDunGen biome:
await PostGen(biome, data, dungeons[^1], reservedTiles, random); await PostGen(biome, dungeons[^1], reservedTiles, random);
break; break;
case BoundaryWallDunGen boundary: case BoundaryWallDunGen boundary:
await PostGen(boundary, data, dungeons[^1], reservedTiles, random); await PostGen(boundary, dungeons[^1], reservedTiles, random);
break; break;
case CornerClutterDunGen clutter: case CornerClutterDunGen clutter:
await PostGen(clutter, data, dungeons[^1], reservedTiles, random); await PostGen(clutter, dungeons[^1], reservedTiles, random);
break; break;
case CorridorClutterDunGen corClutter: case CorridorClutterDunGen corClutter:
await PostGen(corClutter, data, dungeons[^1], reservedTiles, random); await PostGen(corClutter, dungeons[^1], reservedTiles, random);
break; break;
case CorridorDunGen cordor: case CorridorDunGen cordor:
await PostGen(cordor, data, dungeons[^1], reservedTiles, random); await PostGen(cordor, dungeons[^1], reservedTiles, random);
break; break;
case CorridorDecalSkirtingDunGen decks: case CorridorDecalSkirtingDunGen decks:
await PostGen(decks, data, dungeons[^1], reservedTiles, random); await PostGen(decks, dungeons[^1], reservedTiles, random);
break; break;
case EntranceFlankDunGen flank: case EntranceFlankDunGen flank:
await PostGen(flank, data, dungeons[^1], reservedTiles, random); await PostGen(flank, dungeons[^1], reservedTiles, random);
break; break;
case ExteriorDunGen exterior: case ExteriorDunGen exterior:
dungeons.AddRange(await GenerateExteriorDungen(position, exterior, reservedTiles, random)); dungeons.AddRange(await GenerateExteriorDungen(position, exterior, reservedTiles, random));
break; break;
case FillGridDunGen fill: case FillGridDunGen fill:
dungeons.Add(await GenerateFillDunGen(fill, data, reservedTiles)); await GenerateFillDunGen(fill, dungeons, reservedTiles);
break; break;
case JunctionDunGen junc: case JunctionDunGen junc:
await PostGen(junc, data, dungeons[^1], reservedTiles, random); await PostGen(junc, dungeons[^1], reservedTiles, random);
break; break;
case MiddleConnectionDunGen dordor: case MiddleConnectionDunGen dordor:
await PostGen(dordor, data, dungeons[^1], reservedTiles, random); await PostGen(dordor, dungeons[^1], reservedTiles, random);
break; break;
case DungeonEntranceDunGen entrance: case DungeonEntranceDunGen entrance:
await PostGen(entrance, data, dungeons[^1], reservedTiles, random); await PostGen(entrance, dungeons[^1], reservedTiles, random);
break; break;
case ExternalWindowDunGen externalWindow: case ExternalWindowDunGen externalWindow:
await PostGen(externalWindow, data, dungeons[^1], reservedTiles, random); await PostGen(externalWindow, dungeons[^1], reservedTiles, random);
break; break;
case InternalWindowDunGen internalWindow: case InternalWindowDunGen internalWindow:
await PostGen(internalWindow, data, dungeons[^1], reservedTiles, random); await PostGen(internalWindow, dungeons[^1], reservedTiles, random);
break; break;
case MobsDunGen mob: case MobsDunGen mob:
await PostGen(mob, dungeons[^1], random); await PostGen(mob, dungeons[^1], random);
@@ -263,31 +288,40 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
await PostGen(ore, dungeons[^1], random); await PostGen(ore, dungeons[^1], random);
break; break;
case PrefabDunGen prefab: case PrefabDunGen prefab:
dungeons.Add(await GeneratePrefabDunGen(position, data, prefab, reservedTiles, random)); dungeons.Add(await GeneratePrefabDunGen(position, prefab, reservedTiles, random));
break; break;
case PrototypeDunGen prototypo: case PrototypeDunGen prototypo:
var groupConfig = _prototype.Index(prototypo.Proto); var groupConfig = _prototype.Index(prototypo.Proto);
position = (position + random.NextPolarVector2(groupConfig.MinOffset, groupConfig.MaxOffset)).Floored(); position = (position + random.NextPolarVector2(groupConfig.MinOffset, groupConfig.MaxOffset)).Floored();
var dataCopy = groupConfig.Data.Clone(); switch (prototypo.InheritDungeons)
dataCopy.Apply(data); {
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; break;
case ReplaceTileDunGen replace: case ReplaceTileDunGen replace:
dungeons.Add(await GenerateTileReplacementDunGen(replace, data, reservedTiles, random)); await GenerateTileReplacementDunGen(replace, dungeons, reservedTiles, random);
break; break;
case RoomEntranceDunGen rEntrance: case RoomEntranceDunGen rEntrance:
await PostGen(rEntrance, data, dungeons[^1], reservedTiles, random); await PostGen(rEntrance, dungeons[^1], reservedTiles, random);
break; break;
case SplineDungeonConnectorDunGen spline: case SplineDungeonConnectorDunGen spline:
dungeons.Add(await PostGen(spline, data, dungeons, reservedTiles, random)); dungeons.Add(await PostGen(spline, dungeons, reservedTiles, random));
break; break;
case WallMountDunGen wall: case WallMountDunGen wall:
await PostGen(wall, data, dungeons[^1], reservedTiles, random); await PostGen(wall, dungeons[^1], reservedTiles, random);
break; break;
case WormCorridorDunGen worm: case WormCorridorDunGen worm:
await PostGen(worm, data, dungeons[^1], reservedTiles, random); await PostGen(worm, dungeons[^1], reservedTiles, random);
break; break;
default: default:
throw new NotImplementedException(); throw new NotImplementedException();

View File

@@ -9,6 +9,12 @@ public sealed class EntityTableSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = 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) public IEnumerable<EntProtoId> GetSpawns(EntityTableSelector? table, System.Random? rand = null)
{ {
if (table == null) if (table == null)

View File

@@ -5,12 +5,6 @@ namespace Content.Shared.Procedural;
[Virtual, DataDefinition] [Virtual, DataDefinition]
public partial class DungeonConfig public partial class DungeonConfig
{ {
/// <summary>
/// <see cref="Data"/>
/// </summary>
[DataField]
public DungeonData Data = DungeonData.Empty;
/// <summary> /// <summary>
/// The secret sauce, procedural generation layers that get run. /// The secret sauce, procedural generation layers that get run.
/// </summary> /// </summary>

View File

@@ -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,
}

View File

@@ -1,3 +1,5 @@
using Content.Shared.Maps;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonGenerators; 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. /// Places rooms in pre-selected pack layouts. Chooses rooms from the specified whitelist.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// DungeonData keys are:
/// - FallbackTile
/// - Rooms
/// </remarks>
public sealed partial class PrefabDunGen : IDunGenLayer public sealed partial class PrefabDunGen : IDunGenLayer
{ {
/// <summary> /// <summary>
@@ -17,4 +15,10 @@ public sealed partial class PrefabDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField(required: true)] [DataField(required: true)]
public List<ProtoId<DungeonPresetPrototype>> Presets = new(); public List<ProtoId<DungeonPresetPrototype>> Presets = new();
[DataField]
public EntityWhitelist? RoomWhitelist;
[DataField]
public ProtoId<ContentTileDefinition>? FallbackTile;
} }

View File

@@ -8,6 +8,30 @@ namespace Content.Shared.Procedural.DungeonGenerators;
/// </summary> /// </summary>
public sealed partial class PrototypeDunGen : IDunGenLayer 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)] [DataField(required: true)]
public ProtoId<DungeonConfigPrototype> Proto; 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,
}

View File

@@ -1,7 +1,7 @@
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonGenerators; namespace Content.Shared.Procedural.DungeonLayers;
/// <summary> /// <summary>
/// Fills unreserved tiles with the specified entity prototype. /// Fills unreserved tiles with the specified entity prototype.
@@ -17,4 +17,7 @@ public sealed partial class FillGridDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public HashSet<ProtoId<ContentTileDefinition>>? AllowedTiles; public HashSet<ProtoId<ContentTileDefinition>>? AllowedTiles;
[DataField(required: true)]
public EntProtoId Entity;
} }

View File

@@ -1,4 +1,6 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage; using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonLayers; namespace Content.Shared.Procedural.DungeonLayers;
@@ -17,5 +19,5 @@ public sealed partial class MobsDunGen : IDunGenLayer
public int MaxCount = 1; public int MaxCount = 1;
[DataField(required: true)] [DataField(required: true)]
public List<EntitySpawnEntry> Groups = new(); public ProtoId<EntityTablePrototype> Contents;
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Parallax.Biomes; using Content.Shared.Parallax.Biomes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Procedural.Loot; namespace Content.Shared.Procedural.Loot;
@@ -8,6 +9,6 @@ namespace Content.Shared.Procedural.Loot;
/// </summary> /// </summary>
public sealed partial class BiomeTemplateLoot : IDungeonLoot public sealed partial class BiomeTemplateLoot : IDungeonLoot
{ {
[DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<BiomeTemplatePrototype>))] [DataField("proto", required: true)]
public string Prototype = string.Empty; public ProtoId<BiomeTemplatePrototype> Prototype = string.Empty;
} }

View File

@@ -1,10 +1,12 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Runs cables throughout the dungeon. /// Runs cables throughout the dungeon.
/// </summary> /// </summary>
/// <remarks> public sealed partial class AutoCablingDunGen : IDunGenLayer
/// DungeonData keys are: {
/// - Cabling [DataField(required: true)]
/// </remarks> public EntProtoId Entity;
public sealed partial class AutoCablingDunGen : IDunGenLayer; }

View File

@@ -1,3 +1,6 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
@@ -13,6 +16,15 @@ public sealed partial class BoundaryWallDunGen : IDunGenLayer
{ {
[DataField] [DataField]
public BoundaryWallFlags Flags = BoundaryWallFlags.Corridors | BoundaryWallFlags.Rooms; 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] [Flags]

View File

@@ -1,14 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Spawns entities inside corners. /// Spawns entities inside corners.
/// </summary> /// </summary>
/// <remarks>
/// Dungeon data keys are:
/// - CornerClutter
/// </remarks>
public sealed partial class CornerClutterDunGen : IDunGenLayer public sealed partial class CornerClutterDunGen : IDunGenLayer
{ {
[DataField] [DataField]
public float Chance = 0.50f; public float Chance = 0.50f;
[DataField(required:true)]
public ProtoId<EntityTablePrototype> Contents = new();
} }

View File

@@ -1,4 +1,6 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage; using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
@@ -14,5 +16,5 @@ public sealed partial class CorridorClutterDunGen : IDunGenLayer
/// The default starting bulbs /// The default starting bulbs
/// </summary> /// </summary>
[DataField(required: true)] [DataField(required: true)]
public List<EntitySpawnEntry> Contents = new(); public ProtoId<EntityTablePrototype> Contents;
} }

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
@@ -26,4 +22,10 @@ public sealed partial class CorridorDecalSkirtingDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public Dictionary<DirectionFlag, string> CornerDecals = new(); public Dictionary<DirectionFlag, string> CornerDecals = new();
/// <summary>
/// Optional color to apply to the decals.
/// </summary>
[DataField]
public Color? Color;
} }

View File

@@ -1,12 +1,11 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Connects room entrances via corridor segments. /// Connects room entrances via corridor segments.
/// </summary> /// </summary>
/// <remarks>
/// Dungeon data keys are:
/// - FallbackTile
/// </remarks>
public sealed partial class CorridorDunGen : IDunGenLayer public sealed partial class CorridorDunGen : IDunGenLayer
{ {
/// <summary> /// <summary>
@@ -23,4 +22,7 @@ public sealed partial class CorridorDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public float Width = 3f; public float Width = 3f;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
} }

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Selects [count] rooms and places external doors to them. /// Selects [count] rooms and places external doors to them.
/// </summary> /// </summary>
/// <remarks>
/// Dungeon data keys are:
/// - Entrance
/// - FallbackTile
/// </remarks>
public sealed partial class DungeonEntranceDunGen : IDunGenLayer public sealed partial class DungeonEntranceDunGen : IDunGenLayer
{ {
/// <summary> /// <summary>
@@ -15,4 +15,10 @@ public sealed partial class DungeonEntranceDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public int Count = 1; public int Count = 1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
} }

View File

@@ -1,11 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Spawns entities on either side of an entrance. /// Spawns entities on either side of an entrance.
/// </summary> /// </summary>
/// <remarks> public sealed partial class EntranceFlankDunGen : IDunGenLayer
/// Dungeon data keys are: {
/// - FallbackTile [DataField(required: true)]
/// - public ProtoId<ContentTileDefinition> Tile;
/// </remarks>
public sealed partial class EntranceFlankDunGen : IDunGenLayer; [DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents = new();
}

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// If external areas are found will try to generate windows. /// If external areas are found will try to generate windows.
/// </summary> /// </summary>
/// <remarks> public sealed partial class ExternalWindowDunGen : IDunGenLayer
/// Dungeon data keys are: {
/// - EntranceFlank [DataField(required: true)]
/// - FallbackTile public ProtoId<ContentTileDefinition> Tile;
/// </remarks>
public sealed partial class ExternalWindowDunGen : IDunGenLayer; [DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -1,11 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// If internal areas are found will try to generate windows. /// If internal areas are found will try to generate windows.
/// </summary> /// </summary>
/// <remarks> public sealed partial class InternalWindowDunGen : IDunGenLayer
/// Dungeon data keys are: {
/// - FallbackTile [DataField(required: true)]
/// - Window public ProtoId<ContentTileDefinition> Tile;
/// </remarks>
public sealed partial class InternalWindowDunGen : IDunGenLayer; [DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Places the specified entities at junction areas. /// Places the specified entities at junction areas.
/// </summary> /// </summary>
/// <remarks>
/// Dungeon data keys are:
/// - Entrance
/// - FallbackTile
/// </remarks>
public sealed partial class JunctionDunGen : IDunGenLayer public sealed partial class JunctionDunGen : IDunGenLayer
{ {
/// <summary> /// <summary>
@@ -15,4 +15,10 @@ public sealed partial class JunctionDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public int Width = 3; public int Width = 3;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
} }

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
@@ -16,4 +21,13 @@ public sealed partial class MiddleConnectionDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public int Count = 1; public int Count = 1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
[DataField]
public ProtoId<EntityTablePrototype>? Flank;
} }

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
/// Places tiles / entities onto room entrances. /// Places tiles / entities onto room entrances.
/// </summary> /// </summary>
/// <remarks> public sealed partial class RoomEntranceDunGen : IDunGenLayer
/// DungeonData keys are: {
/// - Entrance [DataField(required: true)]
/// - FallbackTile public ProtoId<ContentTileDefinition> Tile;
/// </remarks>
public sealed partial class RoomEntranceDunGen : IDunGenLayer; [DataField]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -1,3 +1,6 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
@@ -5,6 +8,12 @@ namespace Content.Shared.Procedural.PostGeneration;
/// </summary> /// </summary>
public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer
{ {
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField]
public ProtoId<ContentTileDefinition>? WidenTile;
/// <summary> /// <summary>
/// Will divide the distance between the start and end points so that no subdivision is more than these metres away. /// Will divide the distance between the start and end points so that no subdivision is more than these metres away.
/// </summary> /// </summary>

View 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; namespace Content.Shared.Procedural.PostGeneration;
/// <summary> /// <summary>
@@ -10,4 +15,10 @@ public sealed partial class WallMountDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public double Prob = 0.1; public double Prob = 0.1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
} }

View File

@@ -1,3 +1,6 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration; namespace Content.Shared.Procedural.PostGeneration;
// Ime a worm // Ime a worm
@@ -32,4 +35,7 @@ public sealed partial class WormCorridorDunGen : IDunGenLayer
/// </summary> /// </summary>
[DataField] [DataField]
public float Width = 3f; public float Width = 3f;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
} }

View File

@@ -50,12 +50,8 @@ public abstract partial class SharedSalvageSystem
var configProto =_proto.Index(configId); var configProto =_proto.Index(configId);
var layers = new Dictionary<string, int>(); var layers = new Dictionary<string, int>();
var data = new DungeonData();
data.Apply(configProto.Data);
var config = new DungeonConfig var config = new DungeonConfig
{ {
Data = data,
Layers = new(configProto.Layers), Layers = new(configProto.Layers),
MaxCount = configProto.MaxCount, MaxCount = configProto.MaxCount,
MaxOffset = configProto.MaxOffset, MaxOffset = configProto.MaxOffset,

View File

@@ -76,6 +76,18 @@ public static class EntitySpawnCollection
public float CumulativeProbability { get; set; } = 0f; 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> /// <summary>
/// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection. /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection.
/// </summary> /// </summary>

View File

@@ -1,8 +1,11 @@
# Base configs # Setups
- type: dungeonConfig - type: dungeonConfig
id: PlanetBase id: Experiment
layers: layers:
- !type:PrefabDunGen - !type:PrefabDunGen
roomWhitelist:
tags:
- SalvageExperiment
presets: presets:
- Bucket - Bucket
- Wow - Wow
@@ -11,28 +14,49 @@
- !type:CorridorDunGen - !type:CorridorDunGen
width: 3 width: 3
tile: FloorSteel
- !type:DungeonEntranceDunGen - !type:DungeonEntranceDunGen
count: 2 count: 2
tile: FloorSteel
contents: BaseAirlock
- !type:RoomEntranceDunGen - !type:RoomEntranceDunGen
tile: FloorSteel
contents: BaseAirlock
- !type:EntranceFlankDunGen - !type:EntranceFlankDunGen
tile: FloorSteel
contents: BaseWindow
- !type:ExternalWindowDunGen - !type:ExternalWindowDunGen
tile: FloorSteel
contents: BaseWindow
- !type:WallMountDunGen - !type:WallMountDunGen
contents: ScienceLabsWalls
tile: FloorSteel
- !type:BoundaryWallDunGen - !type:BoundaryWallDunGen
wall: WallSolid
cornerWall: WallReinforced
tile: FloorSteel
- !type:JunctionDunGen - !type:JunctionDunGen
width: 1 width: 1
tile: FloorSteel
contents: BaseAirlock
- !type:JunctionDunGen - !type:JunctionDunGen
width: 3
tile: FloorSteel
contents: BaseAirlock
- !type:AutoCablingDunGen - !type:AutoCablingDunGen
entity: CableApcExtension
- !type:CornerClutterDunGen - !type:CornerClutterDunGen
contents: BaseClutter
- !type:CorridorDecalSkirtingDunGen - !type:CorridorDecalSkirtingDunGen
cardinalDecals: cardinalDecals:
@@ -51,44 +75,8 @@
NorthWest: BrickTileWhiteInnerNw NorthWest: BrickTileWhiteInnerNw
NorthEast: BrickTileWhiteInnerNe 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 - type: dungeonConfig
id: Haunted id: Haunted
data:
entities:
Walls: WallRock
tiles:
FallbackTile: FloorCaveDrought
whitelists:
Rooms:
tags:
- Mineshaft
layers: layers:
- !type:PrefabDunGen - !type:PrefabDunGen
presets: presets:
@@ -99,183 +87,304 @@
- !type:WormCorridorDunGen - !type:WormCorridorDunGen
width: 3 width: 3
tile: FloorCaveDrought
- !type:CorridorClutterDunGen - !type:CorridorClutterDunGen
contents: contents: HauntedClutter
- id: FloraStalagmite
- !type:BoundaryWallDunGen - !type:BoundaryWallDunGen
tile: FloorCaveDrought
wall: WallRock
- type: entityTable
id: HauntedClutter
table: !type:GroupSelector
children:
- id: FloraStalagmite
- type: dungeonConfig - type: dungeonConfig
id: LavaBrig 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: layers:
- !type:PrototypeDunGen - !type:PrefabDunGen
proto: PlanetBase 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 - type: dungeonConfig
id: Mineshaft 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: layers:
- !type:PrototypeDunGen - !type:PrefabDunGen
proto: PlanetBase 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 - type: dungeonConfig
id: SnowyLabs 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: layers:
- !type:PrototypeDunGen - !type:PrefabDunGen
proto: PlanetBase 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 # Spawn groups
# Basic # Basic
- type: entitySpawnEntry - type: entityTable
id: BaseClutter id: BaseClutter
entries: table: !type:GroupSelector
- id: PottedPlantRandom children:
amount: 1 - id: PottedPlantRandom
amount: 1
- type: entitySpawnEntry - type: entityTable
id: BaseAirlock id: BaseAirlock
entries: table: !type:GroupSelector
- id: CableApcExtension children:
- id: AirlockGlass - id: CableApcExtension
- id: AirlockGlass
- type: entitySpawnEntry - type: entityTable
id: BaseWindow id: BaseWindow
entries: table: !type:GroupSelector
- id: Grille children:
- id: Window - id: Grille
- id: Window
# Lava brig # Lava brig
- type: entitySpawnEntry - type: entityTable
id: LavaBrigEntrance id: LavaBrigEntrance
entries: table: !type:GroupSelector
- id: CableApcExtension children:
- id: AirlockSecurityGlassLocked - id: CableApcExtension
- id: AirlockSecurityGlassLocked
# Mineshaft # Mineshaft
- type: entitySpawnEntry - type: entityTable
id: BaseWoodWall id: BaseWoodWall
entries: table: !type:GroupSelector
- id: RandomWoodenWall children:
- id: RandomWoodenWall
- type: entitySpawnEntry - type: entityTable
id: BaseWoodSupport id: BaseWoodSupport
entries: table: !type:GroupSelector
- id: RandomWoodenSupport children:
- id: RandomWoodenSupport
- type: entitySpawnEntry - type: entityTable
id: MineshaftClutter id: MineshaftClutter
entries: table: !type:GroupSelector
- id: RandomStalagmiteOrCrystal children:
amount: 1 - id: RandomStalagmiteOrCrystal
amount: 1
- type: entitySpawnEntry - type: entityTable
id: MineshaftWalls id: MineshaftWalls
entries: table: !type:GroupSelector
# Ore children:
- id: WallRockSalt # Ore
prob: 0.6 - id: WallRockSalt
orGroup: content prob: 0.6
- id: WallRockCoal - id: WallRockCoal
prob: 0.6 prob: 0.6
orGroup: content - id: WallRockTin
- id: WallRockTin prob: 0.4
prob: 0.4 - id: WallMining
orGroup: content prob: 0.8
- id: WallMining
prob: 0.8
orGroup: content
# Science lab # Science lab
- type: entitySpawnEntry - type: entityTable
id: ScienceLabsWalls id: ScienceLabsWalls
entries: table: !type:GroupSelector
# Posters children:
- id: RandomPosterLegit # Posters
orGroup: content - id: RandomPosterLegit
- id: ExtinguisherCabinetFilled - id: ExtinguisherCabinetFilled
prob: 0.2 prob: 0.2
orGroup: content - id: RandomPainting
- id: RandomPainting prob: 0.05
prob: 0.05 - id: IntercomCommon
orGroup: content prob: 0.1
- id: IntercomCommon
prob: 0.1
orGroup: content
# Snowy labs # Snowy labs
- type: entitySpawnEntry - type: entityTable
id: SnowyLabsEntrance id: SnowyLabsEntrance
entries: table: !type:GroupSelector
- id: CableApcExtension children:
- id: AirlockFreezerHydroponicsLocked - id: CableApcExtension
- id: AirlockFreezerHydroponicsLocked
- type: entitySpawnEntry - type: entityTable
id: SnowyLabsWalls id: SnowyLabsWalls
entries: table: !type:GroupSelector
# Posters children:
- id: RandomPosterLegit # Posters
orGroup: content - id: RandomPosterLegit
- id: ExtinguisherCabinetFilled - id: ExtinguisherCabinetFilled
prob: 0.2 prob: 0.2
orGroup: content - id: RandomPainting
- id: RandomPainting prob: 0.05
prob: 0.05 - id: IntercomScience
orGroup: content prob: 0.1
- id: IntercomScience
prob: 0.1
orGroup: content

View File

@@ -18,6 +18,7 @@
proto: VGRoidSmaller proto: VGRoidSmaller
- !type:PrototypeDunGen - !type:PrototypeDunGen
proto: VGRoidSmallPaths proto: VGRoidSmallPaths
inheritDungeons: All
- !type:EntityTableDunGen - !type:EntityTableDunGen
minCount: 7 minCount: 7
maxCount: 12 maxCount: 12
@@ -26,6 +27,7 @@
# Fill # Fill
- !type:PrototypeDunGen - !type:PrototypeDunGen
proto: VGRoidFill proto: VGRoidFill
inheritDungeons: All
# Ores # Ores
- !type:OreDunGen - !type:OreDunGen
replacement: IronRock replacement: IronRock
@@ -135,14 +137,12 @@
- type: dungeonConfig - type: dungeonConfig
id: VGRoidExterior id: VGRoidExterior
reserveTiles: true reserveTiles: true
data:
tiles:
FallbackTile: PlatingAsteroid
WidenTile: FloorAsteroidSand
layers: layers:
- !type:PrototypeDunGen - !type:PrototypeDunGen
proto: VGRoidExteriorDungeons proto: VGRoidExteriorDungeons
- !type:SplineDungeonConnectorDunGen - !type:SplineDungeonConnectorDunGen
tile: PlatingAsteroid
widenTile: FloorAsteroidSand
- type: dungeonConfig - type: dungeonConfig
id: VGRoidExteriorDungeons id: VGRoidExteriorDungeons
@@ -185,7 +185,12 @@
- !type:MobsDunGen - !type:MobsDunGen
minCount: 8 minCount: 8
maxCount: 15 maxCount: 15
groups: contents: VGRoidExteriorDungeonsMobs
- type: entityTable
id: VGRoidExteriorDungeonsMobs
table: !type:GroupSelector
children:
- id: SalvageSpawnerMobMiningAsteroid - id: SalvageSpawnerMobMiningAsteroid
amount: 1 amount: 1
@@ -215,7 +220,12 @@
- !type:MobsDunGen - !type:MobsDunGen
minCount: 25 minCount: 25
maxCount: 35 maxCount: 35
groups: contents: VGRoidSmallPathsMobs
- type: entityTable
id: VGRoidSmallPathsMobs
table: !type:GroupSelector
children:
- id: SalvageSpawnerMobMiningAsteroid - id: SalvageSpawnerMobMiningAsteroid
amount: 1 amount: 1
@@ -225,10 +235,8 @@
# Fill with rocks. # Fill with rocks.
- type: dungeonConfig - type: dungeonConfig
id: VGRoidFill id: VGRoidFill
data:
entities:
Fill: IronRock
layers: layers:
- !type:FillGridDunGen - !type:FillGridDunGen
entity: IronRock
allowedTiles: allowedTiles:
- FloorAsteroidSand - FloorAsteroidSand