Rooms demiplane generation improvement (#1583)
* Add CP14RoomsDunGen for procedural room placement Introduces the CP14RoomsDunGen layer for generating rooms across the grid based on tags and tile masks. Updates DungeonJob and DungeonSystem to support the new layer, and modifies relevant YAML prototypes to use CP14RoomsDunGen instead of OreDunGen for room spawning in demiplane modifiers. * some fixes * its done done done * exit room * additional entry points * fix * Update migration.yml
This commit is contained in:
@@ -220,6 +220,9 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
||||
case CP14OreDunGen cp14OreDunGen:
|
||||
await PostGen(cp14OreDunGen, dungeons, reservedTiles, random);
|
||||
break;
|
||||
case CP14RoomsDunGen cp14RoomsDunGen:
|
||||
await PostGen(cp14RoomsDunGen, dungeons, reservedTiles, random);
|
||||
break;
|
||||
//CP14 zone end
|
||||
case AutoCablingDunGen cabling:
|
||||
await PostGen(cabling, dungeons[^1], reservedTiles, random);
|
||||
|
||||
@@ -78,7 +78,7 @@ public sealed partial class DungeonSystem
|
||||
Vector2i origin,
|
||||
DungeonRoomPrototype room,
|
||||
Random random,
|
||||
HashSet<Vector2i>? reservedTiles,
|
||||
HashSet<Vector2i>? reservedTiles = null, //CP14 default null
|
||||
bool clearExisting = false,
|
||||
bool rotation = false)
|
||||
{
|
||||
@@ -129,29 +129,6 @@ public sealed partial class DungeonSystem
|
||||
|
||||
var finalRoomRotation = roomTransform.Rotation();
|
||||
|
||||
// go BRRNNTTT on existing stuff
|
||||
if (clearExisting)
|
||||
{
|
||||
var gridBounds = new Box2(Vector2.Transform(-room.Size/2, roomTransform), Vector2.Transform(room.Size/2, roomTransform));
|
||||
_entitySet.Clear();
|
||||
// Polygon skin moment
|
||||
gridBounds = gridBounds.Enlarged(-0.05f);
|
||||
_lookup.GetLocalEntitiesIntersecting(gridUid, gridBounds, _entitySet, LookupFlags.Uncontained);
|
||||
|
||||
foreach (var templateEnt in _entitySet)
|
||||
{
|
||||
Del(templateEnt);
|
||||
}
|
||||
|
||||
if (TryComp(gridUid, out DecalGridComponent? decalGrid))
|
||||
{
|
||||
foreach (var decal in _decals.GetDecalsIntersecting(gridUid, gridBounds, decalGrid))
|
||||
{
|
||||
_decals.RemoveDecal(gridUid, decal.Index, decalGrid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var roomCenter = (room.Offset + room.Size / 2f) * grid.TileSize;
|
||||
var tileOffset = -roomCenter + grid.TileSizeHalfVector;
|
||||
_tiles.Clear();
|
||||
|
||||
@@ -20,62 +20,59 @@ public sealed partial class DungeonJob
|
||||
HashSet<Vector2i> reservedTiles,
|
||||
Random random)
|
||||
{
|
||||
// Doesn't use dungeon data because layers and we don't need top-down support at the moment.
|
||||
|
||||
var replaceEntities = new Dictionary<Vector2i, EntityUid>();
|
||||
var availableTiles = new List<Vector2i>();
|
||||
var tiles = _maps.GetAllTilesEnumerator(_gridUid, _grid);
|
||||
|
||||
// WARNING:
|
||||
// This DunGen handles not only the tiles of the passed dungeon, but ALL the tiles of the current grid
|
||||
// So don't run it anywhere
|
||||
while (tiles.MoveNext(out var tileRef))
|
||||
foreach (var dun in dungeons)
|
||||
{
|
||||
var tile = tileRef.Value.GridIndices;
|
||||
|
||||
//Tile mask filtering
|
||||
if (gen.TileMask is not null)
|
||||
foreach (var tile in dun.AllTiles)
|
||||
{
|
||||
if (!gen.TileMask.Contains(((ContentTileDefinition) _tileDefManager[tileRef.Value.Tile.TypeId]).ID))
|
||||
continue;
|
||||
}
|
||||
var tileRef = _maps.GetTileRef(_gridUid, _grid, tile);
|
||||
|
||||
//Entity mask filtering
|
||||
if (gen.EntityMask is not null)
|
||||
{
|
||||
var found = false;
|
||||
var enumerator2 = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile);
|
||||
while (enumerator2.MoveNext(out var uid))
|
||||
//Tile mask filtering
|
||||
if (gen.TileMask is not null)
|
||||
{
|
||||
var prototype = _entManager.GetComponent<MetaDataComponent>(uid.Value).EntityPrototype;
|
||||
|
||||
if (prototype?.ID is null)
|
||||
if (!gen.TileMask.Contains(((ContentTileDefinition) _tileDefManager[tileRef.Tile.TypeId]).ID))
|
||||
continue;
|
||||
|
||||
if (!gen.EntityMask.Contains(prototype.ID))
|
||||
continue;
|
||||
|
||||
replaceEntities[tile] = uid.Value;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
//Entity mask filtering
|
||||
if (gen.EntityMask is not null)
|
||||
{
|
||||
var found = false;
|
||||
var enumerator2 = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile);
|
||||
while (enumerator2.MoveNext(out var uid))
|
||||
{
|
||||
var prototype = _entManager.GetComponent<MetaDataComponent>(uid.Value).EntityPrototype;
|
||||
|
||||
if (prototype?.ID is null)
|
||||
continue;
|
||||
|
||||
if (!gen.EntityMask.Contains(prototype.ID))
|
||||
continue;
|
||||
|
||||
replaceEntities[tile] = uid.Value;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If entity mask null - we ignore the tiles that have anything on them.
|
||||
if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add it to valid nodes.
|
||||
availableTiles.Add(tile);
|
||||
|
||||
await SuspendDungeon();
|
||||
|
||||
if (!ValidateResume())
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If entity mask null - we ignore the tiles that have anything on them.
|
||||
if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add it to valid nodes.
|
||||
availableTiles.Add(tile);
|
||||
|
||||
await SuspendDungeon();
|
||||
|
||||
if (!ValidateResume())
|
||||
return;
|
||||
}
|
||||
|
||||
var remapping = new Dictionary<EntProtoId, EntProtoId>();
|
||||
@@ -132,9 +129,9 @@ public sealed partial class DungeonJob
|
||||
|
||||
var prototype = gen.Entity;
|
||||
|
||||
if (replaceEntities.TryGetValue(node, out var existingEnt))
|
||||
if (replaceEntities.TryGetValue(node, out var existingEnt) && _entManager.TryGetComponent<MetaDataComponent>(existingEnt, out var metaData))
|
||||
{
|
||||
var existingProto = _entManager.GetComponent<MetaDataComponent>(existingEnt).EntityPrototype;
|
||||
var existingProto = metaData.EntityPrototype;
|
||||
_entManager.DeleteEntity(existingEnt);
|
||||
|
||||
if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped))
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Procedural;
|
||||
using Content.Shared.Procedural.DungeonLayers;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Procedural.DungeonJob;
|
||||
|
||||
public sealed partial class DungeonJob
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="OreDunGen"/>
|
||||
/// </summary>
|
||||
private async Task PostGen(
|
||||
CP14RoomsDunGen gen,
|
||||
List<Dungeon> dungeons,
|
||||
HashSet<Vector2i> reservedTiles,
|
||||
Random random)
|
||||
{
|
||||
List<DungeonRoomPrototype> availableRooms = new();
|
||||
var availableTiles = new List<Vector2i>();
|
||||
|
||||
foreach (var room in _prototype.EnumeratePrototypes<DungeonRoomPrototype>())
|
||||
{
|
||||
foreach (var tag in room.Tags)
|
||||
{
|
||||
if (gen.Tags.Contains(tag))
|
||||
{
|
||||
availableRooms.Add(room);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (availableRooms.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var dun in dungeons)
|
||||
{
|
||||
foreach (var tile in dun.AllTiles)
|
||||
{
|
||||
var tileRef = _maps.GetTileRef(_gridUid, _grid, tile);
|
||||
|
||||
if (reservedTiles.Contains(tile))
|
||||
continue;
|
||||
|
||||
//Tile mask filtering
|
||||
if (gen.TileMask is not null)
|
||||
{
|
||||
if (!gen.TileMask.Contains(((ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId]).ID))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If entity mask null - we ignore the tiles that have anything on them.
|
||||
if (!_anchorable.TileFree((_gridUid, _grid),
|
||||
tile,
|
||||
DungeonSystem.CollisionLayer,
|
||||
DungeonSystem.CollisionMask))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add it to valid nodes.
|
||||
availableTiles.Add(tile);
|
||||
|
||||
await SuspendDungeon();
|
||||
|
||||
if (!ValidateResume())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < gen.Count && availableTiles.Count > 0; i++)
|
||||
{
|
||||
await SuspendDungeon();
|
||||
|
||||
if (!ValidateResume())
|
||||
return;
|
||||
|
||||
var selectedTile = random.PickAndTake(availableTiles);
|
||||
availableTiles.Remove(selectedTile);
|
||||
|
||||
var room = random.Pick(availableRooms);
|
||||
|
||||
var roomBounds = new List<Vector2i>();
|
||||
var conflict = false;
|
||||
|
||||
for (int x = 0; x < room.Size.X; x++)
|
||||
{
|
||||
for (int y = 0; y < room.Size.Y; y++)
|
||||
{
|
||||
var pos = selectedTile + new Vector2i(x, y);
|
||||
if (reservedTiles.Contains(pos))
|
||||
{
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
roomBounds.Add(pos);
|
||||
}
|
||||
if (conflict) break;
|
||||
}
|
||||
|
||||
if (conflict)
|
||||
continue;
|
||||
|
||||
_dungeon.SpawnRoom(_gridUid, _grid, selectedTile, room, random, clearExisting: true, rotation: true);
|
||||
|
||||
foreach (var pos in roomBounds)
|
||||
{
|
||||
reservedTiles.Add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user