diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 3d566050ed..e813882473 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Construction.Prototypes; using Content.Shared.Examine; using Content.Shared.Input; using Content.Shared.Interaction; +using Content.Shared.Wall; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Player; @@ -182,6 +183,9 @@ namespace Content.Client.Construction sprite.LayerSetSprite(0, prototype.Icon); sprite.LayerSetShader(0, "unshaded"); sprite.LayerSetVisible(0, true); + + if (prototype.CanBuildInImpassable) + EnsureComp(ghost).Arc = new(Math.Tau); } /// diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index f6bd750d3d..a86c3a980a 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -45,6 +45,7 @@ namespace Content.Shared.Interaction [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; + [Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; public const float InteractionRange = 2; @@ -438,22 +439,10 @@ namespace Content.Shared.Interaction CollisionGroup collisionMask = CollisionGroup.Impassable, Ignored? predicate = null, bool popup = false) - { - var originPosition = Transform(origin).MapPosition; - var transform = Transform(other); - var (position, rotation) = transform.GetWorldPositionRotation(); - var mapPos = new MapCoordinates(position, transform.MapID); - var wallPredicate = AddAnchoredPredicate(other, mapPos, rotation, originPosition); + {; + Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false); - Ignored combinedPredicate = e => - { - return e == origin - || e == other - || (predicate?.Invoke(e) ?? false) - || (wallPredicate?.Invoke(e) ?? false); - }; - - var inRange = InRangeUnobstructed(origin, mapPos, range, collisionMask, combinedPredicate, popup); + var inRange = InRangeUnobstructed(Transform(origin).MapPosition, other, range, collisionMask, combinedPredicate); if (!inRange && popup && _gameTiming.IsFirstTimePredicted) { @@ -475,56 +464,43 @@ namespace Content.Shared.Interaction var (position, rotation) = transform.GetWorldPositionRotation(); var mapPos = new MapCoordinates(position, transform.MapID); - var wallPredicate = AddAnchoredPredicate(target, mapPos, rotation, origin); + HashSet ignored = new(); + + bool ignoreAnchored = false; + + if (HasComp(target) && TryComp(target, out PhysicsComponent? physics) && physics.CanCollide) + { + // If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck + // inside of walls, users can still pick them up. + ignored.UnionWith(_sharedBroadphaseSystem.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); + } + else if (TryComp(target, out WallMountComponent? wallMount)) + { + // wall-mount exemptions may be restricted to a specific angle range.da + + if (wallMount.Arc >= Math.Tau) + ignoreAnchored = true; + else + { + var angle = Angle.FromWorldVec(origin.Position - position); + var angleDelta = (wallMount.Direction + rotation - angle).Reduced().FlipPositive(); + ignoreAnchored = angleDelta < wallMount.Arc / 2 || Math.Tau - angleDelta < wallMount.Arc / 2; + } + + if (ignoreAnchored && _mapManager.TryFindGridAt(mapPos, out var grid)) + ignored.UnionWith(grid.GetAnchoredEntities(mapPos)); + } + Ignored combinedPredicate = e => { return e == target || (predicate?.Invoke(e) ?? false) - || (wallPredicate?.Invoke(e) ?? false); + || ignored.Contains(e); }; return InRangeUnobstructed(origin, mapPos, range, collisionMask, combinedPredicate); } - /// - /// If the target entity is either an item or a wall-mounted object, this will add a predicate to ignore any - /// anchored entities on that tile. - /// - public Ignored? AddAnchoredPredicate( - EntityUid target, - MapCoordinates targetPosition, - Angle targetRotation, - MapCoordinates origin) - { - if (!_mapManager.TryFindGridAt(targetPosition, out var grid)) - return null; - - if (HasComp(target)) - { - // Ignore anchored entities on that tile. - var colliding = new HashSet(grid.GetAnchoredEntities(targetPosition)); - return e => colliding.Contains(e); - } - - if (!TryComp(target, out WallMountComponent? wallMount)) - return null; - - // wall-mount exemptions may be restricted to a specific angle range. - if (wallMount.Arc < 360) - { - var angle = Angle.FromWorldVec(origin.Position - targetPosition.Position); - var angleDelta = (wallMount.Direction + targetRotation - angle).Reduced().FlipPositive(); - var inArc = angleDelta < wallMount.Arc / 2 || Math.Tau - angleDelta < wallMount.Arc / 2; - - if (!inArc) - return null; - } - - // Ignore anchored entities on that tile. - var ignored = new HashSet(grid.GetAnchoredEntities(targetPosition)); - return e => ignored.Contains(e); - } - /// /// Checks that an entity and a set of grid coordinates are within a certain /// distance without any entity that matches the collision mask diff --git a/Content.Shared/Wall/WallMountComponent.cs b/Content.Shared/Wall/WallMountComponent.cs index b1ac696f63..a7c5383fc0 100644 --- a/Content.Shared/Wall/WallMountComponent.cs +++ b/Content.Shared/Wall/WallMountComponent.cs @@ -11,11 +11,11 @@ namespace Content.Shared.Wall; public sealed class WallMountComponent : Component { /// - /// Range of angles in which the exemption applies. Bigger is more permissive. + /// Range of angles for which the exemption applies. Bigger is more permissive. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("arc")] - public float Arc = MathF.PI; + public Angle Arc = new(MathF.PI); /// /// The direction in which the exemption arc is facing, relative to the entity's rotation. Defaults to south.