using System.Linq;
using Content.Server.Atmos.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Popups;
using Content.Shared.Atmos;
using Content.Shared.Construction.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
namespace Content.Server.Atmos.EntitySystems;
///
/// This handles restricting pipe-based entities from overlapping outlets/inlets with other entities.
///
public sealed class PipeRestrictOverlapSystem : EntitySystem
{
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TransformSystem _xform = default!;
private readonly List _anchoredEntities = new();
private EntityQuery _nodeContainerQuery;
///
public override void Initialize()
{
SubscribeLocalEvent(OnAnchorStateChanged);
SubscribeLocalEvent(OnAnchorAttempt);
_nodeContainerQuery = GetEntityQuery();
}
private void OnAnchorStateChanged(Entity ent, ref AnchorStateChangedEvent args)
{
if (!args.Anchored)
return;
if (HasComp(ent) && CheckOverlap(ent))
{
_popup.PopupEntity(Loc.GetString("pipe-restrict-overlap-popup-blocked", ("pipe", ent.Owner)), ent);
_xform.Unanchor(ent, Transform(ent));
}
}
private void OnAnchorAttempt(Entity ent, ref AnchorAttemptEvent args)
{
if (args.Cancelled)
return;
if (!_nodeContainerQuery.TryComp(ent, out var node))
return;
var xform = Transform(ent);
if (CheckOverlap((ent, node, xform)))
{
_popup.PopupEntity(Loc.GetString("pipe-restrict-overlap-popup-blocked", ("pipe", ent.Owner)), ent, args.User);
args.Cancel();
}
}
[PublicAPI]
public bool CheckOverlap(EntityUid uid)
{
if (!_nodeContainerQuery.TryComp(uid, out var node))
return false;
return CheckOverlap((uid, node, Transform(uid)));
}
public bool CheckOverlap(Entity ent)
{
if (ent.Comp2.GridUid is not { } grid || !TryComp(grid, out var gridComp))
return false;
var indices = _map.TileIndicesFor(grid, gridComp, ent.Comp2.Coordinates);
_anchoredEntities.Clear();
_map.GetAnchoredEntities((grid, gridComp), indices, _anchoredEntities);
foreach (var otherEnt in _anchoredEntities)
{
// this should never actually happen but just for safety
if (otherEnt == ent.Owner)
continue;
if (!_nodeContainerQuery.TryComp(otherEnt, out var otherComp))
continue;
if (PipeNodesOverlap(ent, (otherEnt, otherComp, Transform(otherEnt))))
return true;
}
return false;
}
public bool PipeNodesOverlap(Entity ent, Entity other)
{
var entDirs = GetAllDirections(ent).ToList();
var otherDirs = GetAllDirections(other).ToList();
foreach (var dir in entDirs)
{
foreach (var otherDir in otherDirs)
{
if ((dir & otherDir) != 0)
return true;
}
}
return false;
IEnumerable GetAllDirections(Entity pipe)
{
foreach (var node in pipe.Comp1.Nodes.Values)
{
// we need to rotate the pipe manually like this because the rotation doesn't update for pipes that are unanchored.
if (node is PipeNode pipeNode)
yield return pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation);
}
}
}
}