From dfb2861f71bdb3836441dda73522bd06e6d37ecd Mon Sep 17 00:00:00 2001
From: Poopp <poopjimpoop@gmail.com>
Date: Fri, 7 Jun 2024 20:46:31 +0300
Subject: [PATCH] Revert "Prevent stacking pipes (#28308)"

This reverts commit 44b93e68ee0d6eb98933b4c8520e7e8e966cce35.
---
 .../PipeRestrictOverlapComponent.cs           |   9 --
 .../PipeRestrictOverlapSystem.cs              | 123 ------------------
 .../ConstructionSystem.Initial.cs             |  39 +++---
 .../NodeContainer/Nodes/PipeNode.cs           |  16 +--
 .../conditions/no-unstackable-in-tile.ftl     |   1 -
 .../Structures/Piping/Atmospherics/binary.yml |   1 -
 .../Structures/Piping/Atmospherics/pipes.yml  |   1 -
 .../Structures/Piping/Atmospherics/unary.yml  |   2 -
 .../Structures/Power/Generation/teg.yml       |   1 -
 9 files changed, 23 insertions(+), 170 deletions(-)
 delete mode 100644 Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs
 delete mode 100644 Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs

diff --git a/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs b/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs
deleted file mode 100644
index 49e1a8c94d3..00000000000
--- a/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Content.Server.Atmos.EntitySystems;
-
-namespace Content.Server.Atmos.Components;
-
-/// <summary>
-/// This is used for restricting anchoring pipes so that they do not overlap.
-/// </summary>
-[RegisterComponent, Access(typeof(PipeRestrictOverlapSystem))]
-public sealed partial class PipeRestrictOverlapComponent : Component;
diff --git a/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs b/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs
deleted file mode 100644
index c2ff87ca79c..00000000000
--- a/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-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;
-
-/// <summary>
-/// This handles restricting pipe-based entities from overlapping outlets/inlets with other entities.
-/// </summary>
-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<EntityUid> _anchoredEntities = new();
-    private EntityQuery<NodeContainerComponent> _nodeContainerQuery;
-
-    /// <inheritdoc/>
-    public override void Initialize()
-    {
-        SubscribeLocalEvent<PipeRestrictOverlapComponent, AnchorStateChangedEvent>(OnAnchorStateChanged);
-        SubscribeLocalEvent<PipeRestrictOverlapComponent, AnchorAttemptEvent>(OnAnchorAttempt);
-
-        _nodeContainerQuery = GetEntityQuery<NodeContainerComponent>();
-    }
-
-    private void OnAnchorStateChanged(Entity<PipeRestrictOverlapComponent> ent, ref AnchorStateChangedEvent args)
-    {
-        if (!args.Anchored)
-            return;
-
-        if (HasComp<AnchorableComponent>(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<PipeRestrictOverlapComponent> 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<NodeContainerComponent, TransformComponent> ent)
-    {
-        if (ent.Comp2.GridUid is not { } grid || !TryComp<MapGridComponent>(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<NodeContainerComponent, TransformComponent> ent, Entity<NodeContainerComponent, TransformComponent> 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<PipeDirection> GetAllDirections(Entity<NodeContainerComponent, TransformComponent> 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);
-            }
-        }
-    }
-}
diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs
index 08dd139ef4c..f299ae212a7 100644
--- a/Content.Server/Construction/ConstructionSystem.Initial.cs
+++ b/Content.Server/Construction/ConstructionSystem.Initial.cs
@@ -16,7 +16,6 @@
 using Content.Shared.Storage;
 using Content.Shared.Whitelist;
 using Robust.Shared.Containers;
-using Robust.Shared.Map;
 using Robust.Shared.Player;
 using Robust.Shared.Timing;
 
@@ -94,14 +93,7 @@ private IEnumerable<EntityUid> EnumerateNearby(EntityUid user)
         }
 
         // LEGACY CODE. See warning at the top of the file!
-        private async Task<EntityUid?> Construct(
-            EntityUid user,
-            string materialContainer,
-            ConstructionGraphPrototype graph,
-            ConstructionGraphEdge edge,
-            ConstructionGraphNode targetNode,
-            EntityCoordinates coords,
-            Angle angle = default)
+        private async Task<EntityUid?> Construct(EntityUid user, string materialContainer, ConstructionGraphPrototype graph, ConstructionGraphEdge edge, ConstructionGraphNode targetNode)
         {
             // We need a place to hold our construction items!
             var container = _container.EnsureContainer<Container>(user, materialContainer, out var existed);
@@ -271,7 +263,7 @@ void ShutdownContainers()
             }
 
             var newEntityProto = graph.Nodes[edge.Target].Entity.GetId(null, user, new(EntityManager));
-            var newEntity = Spawn(newEntityProto, _transformSystem.ToMapCoordinates(coords), rotation: angle);
+            var newEntity = EntityManager.SpawnEntity(newEntityProto, EntityManager.GetComponent<TransformComponent>(user).Coordinates);
 
             if (!TryComp(newEntity, out ConstructionComponent? construction))
             {
@@ -386,13 +378,7 @@ public async Task<bool> TryStartItemConstruction(string prototype, EntityUid use
                 }
             }
 
-            if (await Construct(
-                    user,
-                    "item_construction",
-                    constructionGraph,
-                    edge,
-                    targetNode,
-                    Transform(user).Coordinates) is not { Valid: true } item)
+            if (await Construct(user, "item_construction", constructionGraph, edge, targetNode) is not { Valid: true } item)
                 return false;
 
             // Just in case this is a stack, attempt to merge it. If it isn't a stack, this will just normally pick up
@@ -527,18 +513,23 @@ void Cleanup()
                 return;
             }
 
-            if (await Construct(user,
-                    (ev.Ack + constructionPrototype.GetHashCode()).ToString(),
-                    constructionGraph,
-                    edge,
-                    targetNode,
-                    GetCoordinates(ev.Location),
-                    constructionPrototype.CanRotate ? ev.Angle : Angle.Zero) is not {Valid: true} structure)
+            if (await Construct(user, (ev.Ack + constructionPrototype.GetHashCode()).ToString(), constructionGraph,
+                    edge, targetNode) is not {Valid: true} structure)
             {
                 Cleanup();
                 return;
             }
 
+            // We do this to be able to move the construction to its proper position in case it's anchored...
+            // Oh wow transform anchoring is amazing wow I love it!!!!
+            // ikr
+            var xform = Transform(structure);
+            var wasAnchored = xform.Anchored;
+            xform.Anchored = false;
+            xform.Coordinates = GetCoordinates(ev.Location);
+            xform.LocalRotation = constructionPrototype.CanRotate ? ev.Angle : Angle.Zero;
+            xform.Anchored = wasAnchored;
+
             RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack, GetNetEntity(structure)));
             _adminLogger.Add(LogType.Construction, LogImpact.Low, $"{ToPrettyString(user):player} has turned a {ev.PrototypeName} construction ghost into {ToPrettyString(structure)} at {Transform(structure).Coordinates}");
             Cleanup();
diff --git a/Content.Server/NodeContainer/Nodes/PipeNode.cs b/Content.Server/NodeContainer/Nodes/PipeNode.cs
index 31ee5712493..861f3eea98d 100644
--- a/Content.Server/NodeContainer/Nodes/PipeNode.cs
+++ b/Content.Server/NodeContainer/Nodes/PipeNode.cs
@@ -20,7 +20,7 @@ public partial class PipeNode : Node, IGasMixtureHolder, IRotatableNode
         ///     The directions in which this pipe can connect to other pipes around it.
         /// </summary>
         [DataField("pipeDirection")]
-        public PipeDirection OriginalPipeDirection;
+        private PipeDirection _originalPipeDirection;
 
         /// <summary>
         ///     The *current* pipe directions (accounting for rotation)
@@ -110,26 +110,26 @@ public override void Initialize(EntityUid owner, IEntityManager entMan)
                 return;
 
             var xform = entMan.GetComponent<TransformComponent>(owner);
-            CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(xform.LocalRotation);
+            CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(xform.LocalRotation);
         }
 
         bool IRotatableNode.RotateNode(in MoveEvent ev)
         {
-            if (OriginalPipeDirection == PipeDirection.Fourway)
+            if (_originalPipeDirection == PipeDirection.Fourway)
                 return false;
 
             // update valid pipe direction
             if (!RotationsEnabled)
             {
-                if (CurrentPipeDirection == OriginalPipeDirection)
+                if (CurrentPipeDirection == _originalPipeDirection)
                     return false;
 
-                CurrentPipeDirection = OriginalPipeDirection;
+                CurrentPipeDirection = _originalPipeDirection;
                 return true;
             }
 
             var oldDirection = CurrentPipeDirection;
-            CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(ev.NewRotation);
+            CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(ev.NewRotation);
             return oldDirection != CurrentPipeDirection;
         }
 
@@ -142,12 +142,12 @@ public override void OnAnchorStateChanged(IEntityManager entityManager, bool anc
 
             if (!RotationsEnabled)
             {
-                CurrentPipeDirection = OriginalPipeDirection;
+                CurrentPipeDirection = _originalPipeDirection;
                 return;
             }
 
             var xform = entityManager.GetComponent<TransformComponent>(Owner);
-            CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(xform.LocalRotation);
+            CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(xform.LocalRotation);
         }
 
         public override IEnumerable<Node> GetReachableNodes(TransformComponent xform,
diff --git a/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl b/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl
index 715825e801c..37ce0de9e8e 100644
--- a/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl
+++ b/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl
@@ -1,2 +1 @@
 construction-step-condition-no-unstackable-in-tile = You cannot make a stack of similar devices.
-pipe-restrict-overlap-popup-blocked = { CAPITALIZE(THE($pipe))} doesn't fit over the other pipes!
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
index 213b0b893d9..fa5804c6452 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
@@ -358,7 +358,6 @@
   - type: PipeColorVisuals
   - type: Rotatable
   - type: GasRecycler
-  - type: PipeRestrictOverlap
   - type: NodeContainer
     nodes:
       inlet:
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
index c2fc4e0565b..0025fc5ae1b 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
@@ -51,7 +51,6 @@
   - type: Appearance
   - type: PipeColorVisuals
   - type: NodeContainer
-  - type: PipeRestrictOverlap
   - type: AtmosUnsafeUnanchor
   - type: AtmosPipeColor
   - type: Tag
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
index 2b00fec246f..bffa1549776 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
@@ -246,7 +246,6 @@
       key: enum.ThermomachineUiKey.Key
     - type: WiresPanel
     - type: WiresVisuals
-    - type: PipeRestrictOverlap
     - type: NodeContainer
       nodes:
         pipe:
@@ -421,7 +420,6 @@
   - type: GasCondenser
   - type: AtmosPipeColor
   - type: AtmosDevice
-  - type: PipeRestrictOverlap
   - type: ApcPowerReceiver
     powerLoad: 10000
   - type: Machine
diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml
index 78d979ab8eb..9a378c26a44 100644
--- a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml
@@ -176,7 +176,6 @@
           nodeGroupID: Teg
 
     - type: AtmosUnsafeUnanchor
-    - type: PipeRestrictOverlap
     - type: TegCirculator
     - type: StealTarget
       stealGroup: Teg