Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RoomSpawner mask #33110

Merged
merged 16 commits into from
Feb 12, 2025
Merged
3 changes: 3 additions & 0 deletions Content.IntegrationTests/Tests/EntityTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ await server.WaitPost(() =>
.Where(p => !p.Abstract)
.Where(p => !pair.IsTestPrototype(p))
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Select(p => p.ID)
.ToList();

Expand Down Expand Up @@ -101,6 +102,7 @@ await server.WaitPost(() =>
.Where(p => !p.Abstract)
.Where(p => !pair.IsTestPrototype(p))
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Select(p => p.ID)
.ToList();
foreach (var protoId in protoIds)
Expand Down Expand Up @@ -341,6 +343,7 @@ public async Task AllComponentsOneToOneDeleteTest()
"DebugExceptionInitialize",
"DebugExceptionStartup",
"GridFill",
"RoomFill",
"Map", // We aren't testing a map entity in this test
"MapGrid",
"Broadphase",
Expand Down
56 changes: 31 additions & 25 deletions Content.Server/Procedural/DungeonSystem.Rooms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,20 @@ public sealed partial class DungeonSystem
private readonly List<DungeonRoomPrototype> _availableRooms = new();

/// <summary>
/// Gets a random dungeon room matching the specified area and whitelist.
/// Gets a random dungeon room matching the specified area, whitelist and size.
/// </summary>
public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, Random random, EntityWhitelist? whitelist = null)
{
return GetRoomPrototype(random, whitelist, minSize: size, maxSize: size);
}

/// <summary>
/// Gets a random dungeon room matching the specified area and whitelist and size range
/// </summary>
public DungeonRoomPrototype? GetRoomPrototype(Random random,
EntityWhitelist? whitelist = null,
Vector2i? minSize = null,
Vector2i? maxSize = null)
{
// Can never be true.
if (whitelist is { Tags: null })
Expand All @@ -31,7 +42,10 @@ public sealed partial class DungeonSystem

foreach (var proto in _prototype.EnumeratePrototypes<DungeonRoomPrototype>())
{
if (proto.Size != size)
if (minSize is not null && (proto.Size.X < minSize.Value.X || proto.Size.Y < minSize.Value.Y))
continue;

if (maxSize is not null && (proto.Size.X > maxSize.Value.X || proto.Size.Y > maxSize.Value.Y))
continue;

if (whitelist == null)
Expand Down Expand Up @@ -115,29 +129,6 @@ public void SpawnRoom(

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();
Expand All @@ -156,7 +147,22 @@ public void SpawnRoom(
if (!clearExisting && reservedTiles?.Contains(rounded) == true)
continue;

if (room.IgnoreTile is not null)
{
if (_maps.TryGetTileDef(templateGrid, indices, out var tileDef) && room.IgnoreTile == tileDef.ID)
continue;
}

_tiles.Add((rounded, tileRef.Tile));

if (clearExisting)
{
var anchored = _maps.GetAnchoredEntities((gridUid, grid), rounded);
foreach (var ent in anchored)
{
QueueDel(ent);
}
}
}
}

Expand Down
18 changes: 11 additions & 7 deletions Content.Server/Procedural/RoomFillComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Content.Shared.Procedural;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;

namespace Content.Server.Procedural;

Expand All @@ -18,20 +16,26 @@ public sealed partial class RoomFillComponent : Component
public bool Rotation = true;

/// <summary>
/// Size of the room to fill.
/// Min size of the possible selected room.
/// </summary>
[DataField(required: true)]
public Vector2i Size;
[DataField]
public Vector2i MinSize = new (3, 3);

/// <summary>
/// Max size of the possible selected room.
/// </summary>
[DataField]
public Vector2i MaxSize = new (10, 10);

/// <summary>
/// Rooms allowed for the marker.
/// </summary>
[DataField]
public EntityWhitelist? RoomWhitelist;

/// <summary>
/// Should any existing entities / decals be bulldozed first.
/// </summary>
[DataField]
public bool ClearExisting;
public bool ClearExisting = true;
}
TheShuEd marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 2 additions & 6 deletions Content.Server/Procedural/RoomFillSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,20 @@ public override void Initialize()

private void OnRoomFillMapInit(EntityUid uid, RoomFillComponent component, MapInitEvent args)
{
// Just test things.
if (component.Size == Vector2i.Zero)
return;

var xform = Transform(uid);

if (xform.GridUid != null)
{
var random = new Random();
var room = _dungeon.GetRoomPrototype(component.Size, random, component.RoomWhitelist);
var room = _dungeon.GetRoomPrototype(random, component.RoomWhitelist, component.MinSize, component.MaxSize);

if (room != null)
{
var mapGrid = Comp<MapGridComponent>(xform.GridUid.Value);
_dungeon.SpawnRoom(
xform.GridUid.Value,
mapGrid,
_maps.LocalToTile(xform.GridUid.Value, mapGrid, xform.Coordinates),
_maps.LocalToTile(xform.GridUid.Value, mapGrid, xform.Coordinates) - new Vector2i(room.Size.X/2,room.Size.Y/2),
room,
random,
null,
Expand Down
22 changes: 16 additions & 6 deletions Content.Shared/Procedural/DungeonRoomPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Content.Shared.Maps;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Utility;

namespace Content.Shared.Procedural;
Expand All @@ -10,18 +10,28 @@ public sealed partial class DungeonRoomPrototype : IPrototype
{
[IdDataField] public string ID { get; } = string.Empty;

[ViewVariables(VVAccess.ReadWrite), DataField("tags", customTypeSerializer:typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string> Tags = new();
[ViewVariables(VVAccess.ReadWrite), DataField]
public List<ProtoId<TagPrototype>> Tags = new();

[DataField("size", required: true)] public Vector2i Size;
[DataField(required: true)]
public Vector2i Size;

/// <summary>
/// Path to the file to use for the room.
/// </summary>
[DataField("atlas", required: true)] public ResPath AtlasPath;
[DataField("atlas", required: true)]
public ResPath AtlasPath;

/// <summary>
/// Tile offset into the atlas to use for the room.
/// </summary>
[DataField("offset", required: true)] public Vector2i Offset;
[DataField(required: true)]
public Vector2i Offset;

/// <summary>
/// These tiles will be ignored when copying from the atlas into the actual game,
/// allowing you to make rooms of irregular shapes that blend seamlessly into their surroundings
/// </summary>
[DataField]
public ProtoId<ContentTileDefinition>? IgnoreTile;
}
Loading
Loading