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
40 changes: 33 additions & 7 deletions Content.Server/Procedural/DungeonSystem.Rooms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ namespace Content.Server.Procedural;
public sealed partial class DungeonSystem
{
// Temporary caches.
private readonly HashSet<EntityUid> _entitySet = new();
private readonly List<DungeonRoomPrototype> _availableRooms = new();

/// <summary>
/// Gets a random dungeon room matching the specified area and whitelist.
/// </summary>
public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, Random random, EntityWhitelist? whitelist = null)
public DungeonRoomPrototype? GetRoomPrototype(Random random, EntityWhitelist? whitelist = null, Vector2i? size = null)
{
TheShuEd marked this conversation as resolved.
Show resolved Hide resolved
// Can never be true.
if (whitelist is { Tags: null })
Expand All @@ -31,7 +30,7 @@ public sealed partial class DungeonSystem

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

if (whitelist == null)
Expand Down Expand Up @@ -99,6 +98,20 @@ public Angle GetRoomRotation(DungeonRoomPrototype room, Random random)
return roomRotation;
}

private static Box2 GetRotatedBox(Vector2 point1, Vector2 point2, double angle)
{
if (angle == 0)
return new Box2(point1, point2);
if (Math.Abs(angle - Math.PI / 2) < 1E-5)
return new Box2(point2.X, point1.Y, point1.X, point2.Y);
if (Math.Abs(angle - Math.PI) < 1E-5)
return new Box2(point2, point1);
if (Math.Abs(angle + Math.PI / 2) < 1E-5)
return new Box2(point1.X, point2.Y, point2.X, point1.Y);

throw new NotImplementedException();
}

public void SpawnRoom(
EntityUid gridUid,
MapGridComponent grid,
Expand All @@ -113,18 +126,25 @@ public void SpawnRoom(
var templateGrid = Comp<MapGridComponent>(templateMapUid);
var roomDimensions = room.Size;

var entitySet = new HashSet<EntityUid>();

var finalRoomRotation = roomTransform.Rotation();
TheShuEd marked this conversation as resolved.
Show resolved Hide resolved

// 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();
//The Box2 rotation completely breaks the entity calculation from lookup. and before that, there's a 75% chance the spawn room won't remove anything underneath it.
//Therefore, Box2 must be calculated separately for all 4 rotation options.
var point1 = Vector2.Transform(-room.Size / 2, roomTransform);
var point2 = Vector2.Transform(room.Size / 2, roomTransform);
var gridBounds = GetRotatedBox(point1, point2, finalRoomRotation);

entitySet.Clear();
// Polygon skin moment
gridBounds = gridBounds.Enlarged(-0.05f);
_lookup.GetLocalEntitiesIntersecting(gridUid, gridBounds, _entitySet, LookupFlags.Uncontained);
_lookup.GetLocalEntitiesIntersecting(gridUid, gridBounds, entitySet, LookupFlags.Uncontained);

foreach (var templateEnt in _entitySet)
foreach (var templateEnt in entitySet)
{
Del(templateEnt);
}
Expand Down Expand Up @@ -156,6 +176,12 @@ 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));
}
}
Expand Down
10 changes: 4 additions & 6 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 @@ -20,18 +18,18 @@ public sealed partial class RoomFillComponent : Component
/// <summary>
/// Size of the room to fill.
/// </summary>
[DataField(required: true)]
public Vector2i Size;
[DataField]
public Vector2i? Size;

TheShuEd marked this conversation as resolved.
Show resolved Hide resolved
/// <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
4 changes: 2 additions & 2 deletions Content.Server/Procedural/RoomFillSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ private void OnRoomFillMapInit(EntityUid uid, RoomFillComponent component, MapIn
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.Size);

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