diff --git a/Content.Server/_DV/Salvage/Systems/ShelterCapsuleSystem.cs b/Content.Server/_DV/Salvage/Systems/ShelterCapsuleSystem.cs new file mode 100644 index 0000000000..d0af7543f3 --- /dev/null +++ b/Content.Server/_DV/Salvage/Systems/ShelterCapsuleSystem.cs @@ -0,0 +1,109 @@ +using Content.Server.Fluids.EntitySystems; +using Content.Server.Procedural; +using Content.Shared.Administration.Logs; +using Content.Shared.Chemistry.Components; +using Content.Shared.Database; +using Content.Shared._DV.Salvage.Components; +using Content.Shared._DV.Salvage.Systems; +using Content.Shared.Maps; +using Content.Shared.Physics; +using Robust.Shared.Map.Components; +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; +using System.Numerics; + +namespace Content.Server._DV.Salvage.Systems; + +public sealed class ShelterCapsuleSystem : SharedShelterCapsuleSystem +{ + [Dependency] private readonly DungeonSystem _dungeon = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly SmokeSystem _smoke = default!; + + public static readonly EntProtoId SmokePrototype = "Smoke"; + + private EntityQuery _fixturesQuery; + private HashSet _entities = new(); + + public override void Initialize() + { + base.Initialize(); + + _fixturesQuery = GetEntityQuery(); + } + + protected override LocId? TrySpawnRoom(Entity ent) + { + var xform = Transform(ent); + if (xform.GridUid is not {} gridUid || !TryComp(gridUid, out var grid)) + return "shelter-capsule-error-space"; + + var center = _map.LocalToTile(gridUid, grid, xform.Coordinates); + var room = _proto.Index(ent.Comp.Room); + var origin = center - room.Size / 2; + + // check that every tile it needs isn't blocked + var mask = (int) CollisionGroup.MobMask; + if (IsAreaBlocked(gridUid, center, room.Size, mask)) + return "shelter-capsule-error-obstructed"; + + // check that it isn't on space or SpawnRoom will crash + for (int y = 0; y < room.Size.Y; y++) + { + for (int x = 0; x < room.Size.X; x++) + { + var pos = origin + new Vector2i(x, y); + var tile = _map.GetTileRef((gridUid, grid), pos); + if (tile.Tile.IsEmpty) + return "shelter-capsule-error-space"; + } + } + + var user = ent.Comp.User; + _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(user):user} expanded {ToPrettyString(ent):capsule} at {center} on {ToPrettyString(gridUid):grid}"); + + _dungeon.SpawnRoom(gridUid, + grid, + origin, + room, + new Random(), + null, + clearExisting: true); // already checked for mobs and structures here + + var smoke = Spawn(SmokePrototype, xform.Coordinates); + var spreadAmount = (int) room.Size.Length * 2; + _smoke.StartSmoke(smoke, new Solution(), 3f, spreadAmount); + + QueueDel(ent); + return null; + } + + private bool IsAreaBlocked(EntityUid grid, Vector2i center, Vector2i size, int mask) + { + // This is scaled to 95 % so it doesn't encompass walls on other tiles. + var aabb = Box2.CenteredAround(center, size * 0.95f); + _entities.Clear(); + _lookup.GetLocalEntitiesIntersecting(grid, aabb, _entities, LookupFlags.Dynamic | LookupFlags.Static); + foreach (var uid in _entities) + { + // don't care about non-physical entities + if (!_fixturesQuery.TryComp(uid, out var fixtures)) + continue; + + foreach (var fixture in fixtures.Fixtures.Values) + { + if (!fixture.Hard) + continue; + + if ((fixture.CollisionLayer & mask) != 0) + return true; + } + } + + return false; // no entities colliding with the mask found + } +} diff --git a/Resources/Maps/_DV/Nonstations/shelters.yml b/Resources/Maps/_DV/Nonstations/shelters.yml index 4f81d16745..525069f1e9 100644 --- a/Resources/Maps/_DV/Nonstations/shelters.yml +++ b/Resources/Maps/_DV/Nonstations/shelters.yml @@ -1992,7 +1992,7 @@ entities: - type: Transform pos: 20.5,5.5 parent: 1 -- proto: WindowTintedReinforcedDirectional +- proto: WindowReinforcedDirectional entities: - uid: 325 components: @@ -2099,11 +2099,4 @@ entities: rot: 1.5707963267948966 rad pos: 16.5,-1.5 parent: 1 -- proto: WoodenKegBeer - entities: - - uid: 343 - components: - - type: Transform - pos: 19.5,7.5 - parent: 1 ...