diff --git a/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs b/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs index 6458c6edbc..e42e8585d8 100644 --- a/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs @@ -142,8 +142,7 @@ namespace Content.Client.GameObjects.EntitySystems if (_entityManager.TryGetEntity(args.EntityUid, out var entity)) { // check if the entity is reachable - if (_interactionSystem.InRangeUnobstructed(dragger.Transform.MapPosition, - entity.Transform.MapPosition, ignoredEnt: dragger) == false) + if (!_interactionSystem.InRangeUnobstructed(dragger, entity)) { return false; } @@ -193,8 +192,8 @@ namespace Content.Client.GameObjects.EntitySystems // tell the server we are dropping if we are over a valid drop target in range. // We don't use args.EntityUid here because drag interactions generally should // work even if there's something "on top" of the drop target - if (_interactionSystem.InRangeUnobstructed(_dragger.Transform.MapPosition, - args.Coordinates.ToMap(_mapManager), ignoredEnt: _dragger, ignoreInsideBlocker: true) == false) + if (!_interactionSystem.InRangeUnobstructed(_dragger, + args.Coordinates, ignoreInsideBlocker: true)) { CancelDrag(false, null); return false; @@ -288,8 +287,7 @@ namespace Content.Client.GameObjects.EntitySystems if (anyValidDraggable) { // highlight depending on whether its in or out of range - var inRange = _interactionSystem.InRangeUnobstructed(_dragger.Transform.MapPosition, - pvsEntity.Transform.MapPosition, ignoredEnt: _dragger); + var inRange = _interactionSystem.InRangeUnobstructed(_dragger, pvsEntity); inRangeSprite.PostShader = inRange ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader; inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value; highlightedSprites.Add(inRangeSprite); @@ -377,8 +375,7 @@ namespace Content.Client.GameObjects.EntitySystems return; } // still in range of the thing we are dragging? - if (_interactionSystem.InRangeUnobstructed(_dragger.Transform.MapPosition, - _draggedEntity.Transform.MapPosition, ignoredEnt: _dragger) == false) + if (!_interactionSystem.InRangeUnobstructed(_dragger, _draggedEntity)) { CancelDrag(false, null); return; diff --git a/Content.Client/Instruments/InstrumentMenu.cs b/Content.Client/Instruments/InstrumentMenu.cs index 166ecfaf04..731f80eb87 100644 --- a/Content.Client/Instruments/InstrumentMenu.cs +++ b/Content.Client/Instruments/InstrumentMenu.cs @@ -1,6 +1,7 @@ using Content.Client.GameObjects.Components.Instruments; using Content.Client.UserInterface.Stylesheets; -using Content.Shared.GameObjects.EntitySystems; +using Content.Client.Utility; +using Content.Shared.Utility; using Robust.Client.Audio.Midi; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.UserInterface; @@ -9,7 +10,6 @@ using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Containers; -using Robust.Shared.GameObjects.Systems; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; @@ -197,9 +197,7 @@ namespace Content.Client.Instruments || conMan.Owner != localPlayer.ControlledEntity))) return; // We check that we're in range unobstructed just in case. - if(!EntitySystem.Get() - .InRangeUnobstructed(localPlayer.ControlledEntity.Transform.MapPosition, - instrumentEnt.Transform.MapPosition, ignoredEnt:instrumentEnt)) return; + if (!localPlayer.InRangeUnobstructed(instrumentEnt)) return; if (!_midiManager.IsMidiFile(filename)) { diff --git a/Content.Client/State/GameScreenBase.cs b/Content.Client/State/GameScreenBase.cs index 8b88d26ebc..4caf83d86d 100644 --- a/Content.Client/State/GameScreenBase.cs +++ b/Content.Client/State/GameScreenBase.cs @@ -2,7 +2,8 @@ using System.Collections.Immutable; using System.Linq; using Content.Client.GameObjects.Components; -using Content.Shared.GameObjects.EntitySystems; +using Content.Client.Utility; +using Content.Shared.Utility; using Robust.Client.GameObjects.EntitySystems; using Robust.Client.Interfaces.GameObjects; using Robust.Client.Interfaces.Graphics.ClientEye; @@ -67,13 +68,7 @@ namespace Content.Client.State var inRange = false; if (localPlayer.ControlledEntity != null && entityToClick != null) { - var playerPos = localPlayer.ControlledEntity.Transform.MapPosition; - var entityPos = entityToClick.Transform.MapPosition; - inRange = EntitySystemManager.GetEntitySystem() - .InRangeUnobstructed(playerPos, entityPos, - predicate: entity => - entity == localPlayer.ControlledEntity || entity == entityToClick, - ignoreInsideBlocker: true); + inRange = localPlayer.InRangeUnobstructed(entityToClick, ignoreInsideBlocker: true); } InteractionOutlineComponent outline; diff --git a/Content.Client/Utility/RangeExtensions.cs b/Content.Client/Utility/RangeExtensions.cs new file mode 100644 index 0000000000..c69eb91f6c --- /dev/null +++ b/Content.Client/Utility/RangeExtensions.cs @@ -0,0 +1,93 @@ +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Physics; +using Robust.Client.Player; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.Shared.GameObjects.EntitySystems.SharedInteractionSystem; + +namespace Content.Client.Utility +{ + public static class RangeExtensions + { + private static SharedInteractionSystem SharedInteractionSystem => EntitySystem.Get(); + + public static bool InRangeUnobstructed( + this LocalPlayer origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var otherPosition = other.Transform.MapPosition; + + return origin.InRangeUnobstructed(otherPosition, range, collisionMask, predicate, ignoreInsideBlocker, + popup); + } + + public static bool InRangeUnobstructed( + this LocalPlayer origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return origin.InRangeUnobstructed(other.Owner, range, collisionMask, predicate, ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this LocalPlayer origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return origin.InRangeUnobstructed(other.Owner, range, collisionMask, predicate, ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this LocalPlayer origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var mapManager = IoCManager.Resolve(); + var otherPosition = other.ToMap(mapManager); + + return origin.InRangeUnobstructed(otherPosition, range, collisionMask, predicate, ignoreInsideBlocker, + popup); + } + + public static bool InRangeUnobstructed( + this LocalPlayer origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.ControlledEntity; + if (originEntity == null) + { + // TODO: Take into account the player's camera position? + return false; + } + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + } +} diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs new file mode 100644 index 0000000000..6ff7cfba99 --- /dev/null +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -0,0 +1,159 @@ +using System.Threading.Tasks; +using Content.Client.Utility; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Utility; +using NUnit.Framework; +using Robust.Server.GameObjects.Components.Container; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.Interaction +{ + [TestFixture] + [TestOf(typeof(SharedInteractionSystem))] + [TestOf(typeof(SharedRangeExtensions))] + [TestOf(typeof(RangeExtensions))] + public class InRangeUnobstructed : ContentIntegrationTest + { + private const string HumanId = "BaseHumanMob_Content"; + + private const float InteractionRange = SharedInteractionSystem.InteractionRange; + + private const float InteractionRangeDivided15 = InteractionRange / 1.5f; + + private readonly (float, float) _interactionRangeDivided15X = (InteractionRangeDivided15, 0f); + + private const float InteractionRangeDivided15Times3 = InteractionRangeDivided15 * 3; + + [Test] + public async Task EntityEntityTest() + { + var server = StartServerDummyTicker(); + + await server.WaitIdleAsync(); + + var entityManager = server.ResolveDependency(); + var mapManager = server.ResolveDependency(); + + IEntity origin = null; + IEntity other = null; + IContainer container = null; + IComponent component = null; + GridCoordinates gridCoordinates = default; + MapCoordinates mapCoordinates = default; + + server.Assert(() => + { + mapManager.CreateNewMapEntity(MapId.Nullspace); + var coordinates = MapCoordinates.Nullspace; + + origin = entityManager.SpawnEntity(HumanId, coordinates); + other = entityManager.SpawnEntity(HumanId, coordinates); + container = ContainerManagerComponent.Ensure("InRangeUnobstructedTestOtherContainer", other); + component = other.Transform; + gridCoordinates = other.Transform.GridPosition; + mapCoordinates = other.Transform.MapPosition; + }); + + await server.WaitIdleAsync(); + + server.Assert(() => + { + // Entity <-> Entity + Assert.True(origin.InRangeUnobstructed(other)); + Assert.True(other.InRangeUnobstructed(origin)); + + // Entity <-> Component + Assert.True(origin.InRangeUnobstructed(component)); + Assert.True(component.InRangeUnobstructed(origin)); + + // Entity <-> Container + Assert.True(origin.InRangeUnobstructed(container)); + Assert.True(container.InRangeUnobstructed(origin)); + + // Entity <-> GridCoordinates + Assert.True(origin.InRangeUnobstructed(gridCoordinates)); + Assert.True(gridCoordinates.InRangeUnobstructed(origin)); + + // Entity <-> MapCoordinates + Assert.True(origin.InRangeUnobstructed(mapCoordinates)); + Assert.True(mapCoordinates.InRangeUnobstructed(origin)); + + + // Move them slightly apart + origin.Transform.LocalPosition += _interactionRangeDivided15X; + + // Entity <-> Entity + Assert.True(origin.InRangeUnobstructed(other)); + Assert.True(other.InRangeUnobstructed(origin)); + + // Entity <-> Component + Assert.True(origin.InRangeUnobstructed(component)); + Assert.True(component.InRangeUnobstructed(origin)); + + // Entity <-> Container + Assert.True(origin.InRangeUnobstructed(container)); + Assert.True(container.InRangeUnobstructed(origin)); + + // Entity <-> GridCoordinates + Assert.True(origin.InRangeUnobstructed(gridCoordinates)); + Assert.True(gridCoordinates.InRangeUnobstructed(origin)); + + // Entity <-> MapCoordinates + Assert.True(origin.InRangeUnobstructed(mapCoordinates)); + Assert.True(mapCoordinates.InRangeUnobstructed(origin)); + + + // Move them out of range + origin.Transform.LocalPosition += _interactionRangeDivided15X; + + // Entity <-> Entity + Assert.False(origin.InRangeUnobstructed(other)); + Assert.False(other.InRangeUnobstructed(origin)); + + // Entity <-> Component + Assert.False(origin.InRangeUnobstructed(component)); + Assert.False(component.InRangeUnobstructed(origin)); + + // Entity <-> Container + Assert.False(origin.InRangeUnobstructed(container)); + Assert.False(container.InRangeUnobstructed(origin)); + + // Entity <-> GridCoordinates + Assert.False(origin.InRangeUnobstructed(gridCoordinates)); + Assert.False(gridCoordinates.InRangeUnobstructed(origin)); + + // Entity <-> MapCoordinates + Assert.False(origin.InRangeUnobstructed(mapCoordinates)); + Assert.False(mapCoordinates.InRangeUnobstructed(origin)); + + + // Checks with increased range + + // Entity <-> Entity + Assert.True(origin.InRangeUnobstructed(other, InteractionRangeDivided15Times3)); + Assert.True(other.InRangeUnobstructed(origin, InteractionRangeDivided15Times3)); + + // Entity <-> Component + Assert.True(origin.InRangeUnobstructed(component, InteractionRangeDivided15Times3)); + Assert.True(component.InRangeUnobstructed(origin, InteractionRangeDivided15Times3)); + + // Entity <-> Container + Assert.True(origin.InRangeUnobstructed(container, InteractionRangeDivided15Times3)); + Assert.True(container.InRangeUnobstructed(origin, InteractionRangeDivided15Times3)); + + // Entity <-> GridCoordinates + Assert.True(origin.InRangeUnobstructed(gridCoordinates, InteractionRangeDivided15Times3)); + Assert.True(gridCoordinates.InRangeUnobstructed(origin, InteractionRangeDivided15Times3)); + + // Entity <-> MapCoordinates + Assert.True(origin.InRangeUnobstructed(mapCoordinates, InteractionRangeDivided15Times3)); + Assert.True(mapCoordinates.InRangeUnobstructed(origin, InteractionRangeDivided15Times3)); + }); + + await server.WaitIdleAsync(); + } + } +} diff --git a/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs b/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs index 8753fb6115..477c878491 100644 --- a/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs +++ b/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs @@ -3,6 +3,7 @@ using Content.Server.AI.WorldState.States.Inventory; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.Utility; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Shared.Interfaces.GameObjects; namespace Content.Server.AI.Operators.Inventory @@ -50,7 +51,7 @@ namespace Content.Server.AI.Operators.Inventory public override Outcome Execute(float frameTime) { - if (!InteractionChecks.InRangeUnobstructed(_owner, _target.Transform.MapPosition)) + if (!_owner.InRangeUnobstructed(_target, popup: true)) { return Outcome.Failed; } diff --git a/Content.Server/AI/Operators/Inventory/InteractWithEntityOperator.cs b/Content.Server/AI/Operators/Inventory/InteractWithEntityOperator.cs index 8e49c9587c..94702d95d6 100644 --- a/Content.Server/AI/Operators/Inventory/InteractWithEntityOperator.cs +++ b/Content.Server/AI/Operators/Inventory/InteractWithEntityOperator.cs @@ -1,6 +1,7 @@ using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.Utility; +using Content.Shared.Utility; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -28,7 +29,7 @@ namespace Content.Server.AI.Operators.Inventory return Outcome.Failed; } - if (!InteractionChecks.InRangeUnobstructed(_owner, _useTarget.Transform.MapPosition)) + if (!_owner.InRangeUnobstructed(_useTarget, popup: true)) { return Outcome.Failed; } diff --git a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs index f909842264..d981b843e6 100644 --- a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs +++ b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs @@ -4,6 +4,7 @@ using Content.Server.AI.WorldState.States.Inventory; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.Utility; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Shared.Containers; using Robust.Shared.Interfaces.GameObjects; @@ -30,7 +31,7 @@ namespace Content.Server.AI.Operators.Inventory return Outcome.Success; } - if (!InteractionChecks.InRangeUnobstructed(_owner, container.Owner.Transform.MapPosition, ignoredEnt: container.Owner)) + if (!_owner.InRangeUnobstructed(container, popup: true)) { return Outcome.Failed; } diff --git a/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs b/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs index d6631a41d7..289a9ba96a 100644 --- a/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs +++ b/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs @@ -3,6 +3,7 @@ using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.Utility; +using Content.Shared.Utility; using Robust.Shared.Containers; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -26,7 +27,7 @@ namespace Content.Server.AI.Operators.Inventory if (_target.Deleted || !_target.HasComponent() || ContainerHelpers.IsInContainer(_target) || - !InteractionChecks.InRangeUnobstructed(_owner, _target.Transform.MapPosition)) + !_owner.InRangeUnobstructed(_target, popup: true)) { return Outcome.Failed; } diff --git a/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs b/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs index f6e979557a..890d2d576f 100644 --- a/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs +++ b/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs @@ -1,5 +1,4 @@ - -using Robust.Server.GameObjects; +using Robust.Server.GameObjects; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Robust.Shared.GameObjects; @@ -22,6 +21,8 @@ using Content.Shared.GameObjects.Components.Mobs; using Robust.Shared.Maths; using System; using System.Collections.Generic; +using Content.Shared.Utility; +using Serilog; using Content.Server.GameObjects.Components.GUI; namespace Content.Server.GameObjects.Components.ActionBlocking @@ -109,11 +110,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return; } - if (!EntitySystem.Get().InRangeUnobstructed( - handcuff.Transform.MapPosition, - Owner.Transform.MapPosition, - _interactRange, - ignoredEnt: Owner)) + if (!handcuff.InRangeUnobstructed(Owner, _interactRange)) { Logger.Warning("Handcuffs being applied to player are obstructed or too far away! This should not happen!"); return; @@ -131,7 +128,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// /// Check the current amount of hands the owner has, and if there's less hands than active cuffs we remove some cuffs. /// - private void UpdateHandCount() + private void UpdateHandCount() { var dirty = false; var handCount = _hands.Hands.Count(); @@ -238,22 +235,13 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return; } - if (!isOwner && - !EntitySystem.Get().InRangeUnobstructed( - user.Transform.MapPosition, - Owner.Transform.MapPosition, - _interactRange, - ignoredEnt: Owner)) + if (!isOwner && user.InRangeUnobstructed(Owner, _interactRange)) { user.PopupMessage(user, Loc.GetString("You are too far away to remove the cuffs.")); return; } - if (!EntitySystem.Get().InRangeUnobstructed( - cuffsToRemove.Transform.MapPosition, - Owner.Transform.MapPosition, - _interactRange, - ignoredEnt: Owner)) + if (!cuffsToRemove.InRangeUnobstructed(Owner, _interactRange)) { Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!"); return; diff --git a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs index ca05cf37dc..2f7d8e6aab 100644 --- a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs +++ b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs @@ -16,6 +16,7 @@ using Content.Shared.GameObjects.Components.ActionBlocking; using Content.Server.GameObjects.Components.Mobs; using Robust.Shared.Maths; using System; +using Content.Shared.Utility; namespace Content.Server.GameObjects.Components.ActionBlocking { @@ -115,7 +116,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking private float _interactRange; private DoAfterSystem _doAfterSystem; private AudioSystem _audioSystem; - + public override void Initialize() { base.Initialize(); @@ -182,11 +183,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return; } - if (!EntitySystem.Get().InRangeUnobstructed( - eventArgs.User.Transform.MapPosition, - eventArgs.Target.Transform.MapPosition, - _interactRange, - ignoredEnt: Owner)) + if (!eventArgs.InRangeUnobstructed(_interactRange, ignoreInsideBlocker: true)) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are too far away to use the cuffs!")); return; @@ -196,7 +193,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking _notifyManager.PopupMessage(eventArgs.User, eventArgs.Target, Loc.GetString("{0:theName} starts cuffing you!", eventArgs.User)); _audioSystem.PlayFromEntity(StartCuffSound, Owner); - TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed); + TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed); } /// diff --git a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs index e32be62caa..e46b2c576b 100644 --- a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs +++ b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs @@ -7,13 +7,13 @@ using Content.Server.GameObjects.Components.Mobs.State; using Content.Server.GameObjects.Components.Strap; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; -using Content.Server.Utility; using Content.Shared.GameObjects.Components.Buckle; using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystemMessages; using Robust.Server.GameObjects.EntitySystems; @@ -190,13 +190,10 @@ namespace Content.Server.GameObjects.Components.Buckle return false; } - var ownerPosition = Owner.Transform.MapPosition; - var strapPosition = strap.Owner.Transform.MapPosition; - var interaction = EntitySystem.Get(); var component = strap; bool Ignored(IEntity entity) => entity == Owner || entity == user || entity == component.Owner; - if (!interaction.InRangeUnobstructed(ownerPosition, strapPosition, _range, predicate: Ignored)) + if (!Owner.InRangeUnobstructed(strap, _range, predicate: Ignored, popup: true)) { _notifyManager.PopupMessage(strap.Owner, user, Loc.GetString("You can't reach there!")); @@ -346,9 +343,7 @@ namespace Content.Server.GameObjects.Components.Buckle return false; } - var strapPosition = Owner.Transform.MapPosition; - - if (!InteractionChecks.InRangeUnobstructed(user, strapPosition, _range)) + if (!user.InRangeUnobstructed(oldBuckledTo, _range, popup: true)) { return false; } diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index a3a9959ca1..d081e498a9 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -2,10 +2,10 @@ using System; using Content.Server.GameObjects.Components.Body.Circulatory; using Content.Server.Interfaces; -using Content.Server.Utility; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -111,7 +111,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; //Make sure we have the attacking entity if (eventArgs.Target == null || !Owner.TryGetComponent(out SolutionComponent? solution) || !solution.Injector) diff --git a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs index 0704436578..170795c469 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs @@ -5,6 +5,7 @@ using Content.Server.Utility; using Content.Shared.Chemistry; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -80,7 +81,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; } - if (!InteractionChecks.InRangeUnobstructed(user, trueTarget.Transform.MapPosition)) + if (!user.InRangeUnobstructed(trueTarget, popup: true)) { return false; } diff --git a/Content.Server/GameObjects/Components/Fluids/MopComponent.cs b/Content.Server/GameObjects/Components/Fluids/MopComponent.cs index 338af8611c..907c1d3a48 100644 --- a/Content.Server/GameObjects/Components/Fluids/MopComponent.cs +++ b/Content.Server/GameObjects/Components/Fluids/MopComponent.cs @@ -4,6 +4,7 @@ using Content.Server.Utility; using Content.Shared.Chemistry; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; @@ -68,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Fluids void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (!Owner.TryGetComponent(out SolutionComponent? contents)) return; - if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; if (CurrentVolume <= 0) { diff --git a/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs index 954c1ed639..d8e69e964d 100644 --- a/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Maps; +using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; @@ -42,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Interactable var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); - if (!_entitySystemManager.GetEntitySystem().InRangeUnobstructed(user.Transform.MapPosition, coordinates.ToMap(_mapManager), ignoredEnt:user)) + if (!user.InRangeUnobstructed(coordinates, popup: true)) return; var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; diff --git a/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs b/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs index 8321016565..d39515349f 100644 --- a/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs @@ -2,6 +2,7 @@ using Content.Server.Utility; using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Maps; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; @@ -35,7 +36,7 @@ namespace Content.Server.GameObjects.Components.Items public void AfterInteract(AfterInteractEventArgs eventArgs) { - if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; if (!Owner.TryGetComponent(out StackComponent stack)) return; var attacked = eventArgs.Target; diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs index 40f2b36722..d8194508e4 100644 --- a/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs @@ -6,6 +6,7 @@ using Content.Server.Utility; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Maps; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; @@ -168,7 +169,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD } var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); - if (coordinates == GridCoordinates.InvalidGrid || !InteractionChecks.InRangeUnobstructed(eventArgs)) + if (coordinates == GridCoordinates.InvalidGrid || !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { return false; } diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index 4eead02f0f..265d925ad1 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -8,6 +8,7 @@ using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.Containers; using Robust.Shared.GameObjects; @@ -96,9 +97,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage return false; } - var itemPos = Owner.Transform.MapPosition; - - return InteractionChecks.InRangeUnobstructed(user, itemPos, ignoredEnt: Owner, ignoreInsideBlocker:true); + return user.InRangeUnobstructed(Owner, ignoreInsideBlocker: true, popup: true); } public bool InteractHand(InteractHandEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 719c361083..6b8acac68a 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -11,6 +11,7 @@ using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.EntitySystemMessages; @@ -405,9 +406,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage break; } - var storagePosition = Owner.Transform.MapPosition; - - if (!InteractionChecks.InRangeUnobstructed(player, storagePosition)) + if (!player.InRangeUnobstructed(Owner, popup: true)) { break; } diff --git a/Content.Server/GameObjects/Components/Medical/HealingComponent.cs b/Content.Server/GameObjects/Components/Medical/HealingComponent.cs index 8768acc071..ea1eb95f89 100644 --- a/Content.Server/GameObjects/Components/Medical/HealingComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/HealingComponent.cs @@ -5,6 +5,7 @@ using Content.Shared.Damage; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; @@ -43,18 +44,10 @@ namespace Content.Server.GameObjects.Components.Medical return; } - if (eventArgs.User != eventArgs.Target) + if (eventArgs.User != eventArgs.Target && + !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { - var interactionSystem = EntitySystem.Get(); - var from = eventArgs.User.Transform.MapPosition; - var to = eventArgs.Target.Transform.MapPosition; - bool Ignored(IEntity entity) => entity == eventArgs.User || entity == eventArgs.Target; - var inRange = interactionSystem.InRangeUnobstructed(from, to, predicate: Ignored); - - if (!inRange) - { - return; - } + return; } if (Owner.TryGetComponent(out StackComponent stack) && diff --git a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs index 2b2cf18bbd..cf7f44b432 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.GameObjects.Components.Body; +using Content.Server.GameObjects.Components.Body; using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.EntitySystems; @@ -18,6 +18,7 @@ using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; using System; using Content.Server.Utility; +using Content.Shared.Utility; namespace Content.Server.GameObjects.Components.Movement { @@ -107,12 +108,7 @@ namespace Content.Server.GameObjects.Components.Movement return false; } - var userPosition = user.Transform.MapPosition; - var climbablePosition = target.Transform.MapPosition; - var interaction = EntitySystem.Get(); - bool Ignored(IEntity entity) => (entity == target || entity == user); - - if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored)) + if (!user.InRangeUnobstructed(target, _range)) { reason = Loc.GetString("You can't reach there!"); return false; @@ -144,14 +140,10 @@ namespace Content.Server.GameObjects.Components.Movement return false; } - var userPosition = user.Transform.MapPosition; - var otherUserPosition = dragged.Transform.MapPosition; - var climbablePosition = target.Transform.MapPosition; - var interaction = EntitySystem.Get(); - bool Ignored(IEntity entity) => (entity == target || entity == user || entity == dragged); + bool Ignored(IEntity entity) => entity == target || entity == user || entity == dragged; - if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) || - !interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored)) + if (!user.InRangeUnobstructed(target, _range, predicate: Ignored) || + !user.InRangeUnobstructed(dragged, _range, predicate: Ignored)) { reason = Loc.GetString("You can't reach there!"); return false; diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index cec956d208..6604f0ea1e 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -11,6 +11,7 @@ using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Utensil; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -162,7 +163,7 @@ namespace Content.Server.GameObjects.Components.Nutrition } } - if (!InteractionChecks.InRangeUnobstructed(user, trueTarget.Transform.MapPosition)) + if (!user.InRangeUnobstructed(trueTarget, popup: true)) { return false; } diff --git a/Content.Server/GameObjects/Components/Power/AME/AMEControllerComponent.cs b/Content.Server/GameObjects/Components/Power/AME/AMEControllerComponent.cs index 92ab6732fd..b8a768d728 100644 --- a/Content.Server/GameObjects/Components/Power/AME/AMEControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/AME/AMEControllerComponent.cs @@ -3,7 +3,6 @@ using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.NodeContainer; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; -using Content.Server.GameObjects.Components.NodeContainer.Nodes; using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.PowerNetComponents; using Content.Server.Interfaces; @@ -12,7 +11,6 @@ using Content.Server.Utility; using Content.Shared.GameObjects.Components.Power.AME; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; -using Microsoft.EntityFrameworkCore.Internal; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.UserInterface; @@ -20,17 +18,12 @@ using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.Audio; using Robust.Shared.GameObjects; -using Robust.Shared.GameObjects.Components.Transform; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.ViewVariables; -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Content.Server.GameObjects.Components.Power.AME @@ -87,20 +80,27 @@ namespace Content.Server.GameObjects.Components.Power.AME return; } + var group = GetAMENodeGroup(); + + if (group == null) + { + return; + } + _jarSlot.ContainedEntity.TryGetComponent(out var fuelJar); if(fuelJar != null && _powerSupplier != null && fuelJar.FuelAmount > InjectionAmount) { - _powerSupplier.SupplyRate = GetAMENodeGroup().InjectFuel(InjectionAmount); + _powerSupplier.SupplyRate = group.InjectFuel(InjectionAmount); fuelJar.FuelAmount -= InjectionAmount; InjectSound(); UpdateUserInterface(); } - _stability = GetAMENodeGroup().GetTotalStability(); + _stability = group.GetTotalStability(); UpdateDisplay(_stability); - if(_stability <= 0) { GetAMENodeGroup().ExplodeCores(); } + if(_stability <= 0) { group.ExplodeCores(); } } @@ -213,7 +213,7 @@ namespace Content.Server.GameObjects.Components.Power.AME break; } - GetAMENodeGroup().UpdateCoreVisuals(InjectionAmount, _injecting); + GetAMENodeGroup()?.UpdateCoreVisuals(InjectionAmount, _injecting); UpdateUserInterface(); ClickSound(); @@ -272,11 +272,11 @@ namespace Content.Server.GameObjects.Components.Power.AME private void RefreshParts() { - GetAMENodeGroup().RefreshAMENodes(this); + GetAMENodeGroup()?.RefreshAMENodes(this); UpdateUserInterface(); } - private AMENodeGroup GetAMENodeGroup() + private AMENodeGroup? GetAMENodeGroup() { Owner.TryGetComponent(out NodeContainerComponent? nodeContainer); @@ -285,12 +285,12 @@ namespace Content.Server.GameObjects.Components.Power.AME .OfType() .First(); - return engineNodeGroup ?? default!; + return engineNodeGroup; } private bool IsMasterController() { - if(GetAMENodeGroup().MasterController == this) + if(GetAMENodeGroup()?.MasterController == this) { return true; } @@ -301,10 +301,11 @@ namespace Content.Server.GameObjects.Components.Power.AME private int GetCoreCount() { var coreCount = 0; + var group = GetAMENodeGroup(); - if(GetAMENodeGroup() != null) + if (group != null) { - coreCount = GetAMENodeGroup().CoreCount; + coreCount = group.CoreCount; } return coreCount; diff --git a/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs b/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs index bed8295a06..5b79399c9d 100644 --- a/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs @@ -1,6 +1,7 @@ using Content.Server.GameObjects.Components.Stack; using Content.Server.Utility; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.Transform; @@ -36,7 +37,7 @@ namespace Content.Server.GameObjects.Components.Power /// public void AfterInteract(AfterInteractEventArgs eventArgs) { - if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; if(!_mapManager.TryGetGrid(eventArgs.ClickLocation.GridID, out var grid)) return; var snapPos = grid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center); diff --git a/Content.Server/GameObjects/Components/Strap/StrapComponent.cs b/Content.Server/GameObjects/Components/Strap/StrapComponent.cs index ea47b05290..45a6346ada 100644 --- a/Content.Server/GameObjects/Components/Strap/StrapComponent.cs +++ b/Content.Server/GameObjects/Components/Strap/StrapComponent.cs @@ -4,6 +4,7 @@ using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; @@ -202,14 +203,9 @@ namespace Content.Server.GameObjects.Components.Strap parent = parent.Parent; } - var userPosition = user.Transform.MapPosition; - var strapPosition = component.Owner.Transform.MapPosition; var range = SharedInteractionSystem.InteractionRange / 2; - var inRange = EntitySystem.Get() - .InRangeUnobstructed(userPosition, strapPosition, range, - predicate: entity => entity == user || entity == component.Owner); - if (!inRange) + if (!user.InRangeUnobstructed(component, range)) { return; } diff --git a/Content.Server/GameObjects/Components/Utensil/UtensilComponent.cs b/Content.Server/GameObjects/Components/Utensil/UtensilComponent.cs index b530ce5d22..8fadffc4d1 100644 --- a/Content.Server/GameObjects/Components/Utensil/UtensilComponent.cs +++ b/Content.Server/GameObjects/Components/Utensil/UtensilComponent.cs @@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components.Nutrition; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Utensil; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -121,7 +122,7 @@ namespace Content.Server.GameObjects.Components.Utensil return; } - if (!InteractionChecks.InRangeUnobstructed(user, target.Transform.MapPosition)) + if (!user.InRangeUnobstructed(target, popup: true)) { return; } diff --git a/Content.Server/GameObjects/Components/Weapon/FlashableComponent.cs b/Content.Server/GameObjects/Components/Weapon/FlashableComponent.cs index d94e9b87ae..b09d95aa19 100644 --- a/Content.Server/GameObjects/Components/Weapon/FlashableComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/FlashableComponent.cs @@ -1,6 +1,6 @@ using System; -using Content.Server.Utility; using Content.Shared.GameObjects.Components.Weapons; +using Content.Shared.Utility; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -32,7 +32,7 @@ namespace Content.Server.GameObjects.Components.Weapon { foreach (var entity in IoCManager.Resolve().GetEntitiesInRange(source.Transform.GridPosition, range)) { - if (!InteractionChecks.InRangeUnobstructed(source, entity.Transform.MapPosition, range, ignoredEnt:entity)) + if (!source.InRangeUnobstructed(entity, range, popup: true)) continue; if(entity.TryGetComponent(out FlashableComponent flashable)) diff --git a/Content.Server/GameObjects/Components/WiresComponent.cs b/Content.Server/GameObjects/Components/WiresComponent.cs index 50ea153a2d..3ad387a7ba 100644 --- a/Content.Server/GameObjects/Components/WiresComponent.cs +++ b/Content.Server/GameObjects/Components/WiresComponent.cs @@ -13,6 +13,7 @@ using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.UserInterface; @@ -388,7 +389,7 @@ namespace Content.Server.GameObjects.Components return; } - if (!EntitySystem.Get().InRangeUnobstructed(player.Transform.MapPosition, Owner.Transform.MapPosition, ignoredEnt: Owner)) + if (!player.InRangeUnobstructed(Owner)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, Loc.GetString("You can't reach there!")); diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs index fbb36045ab..2d852011b0 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs @@ -7,8 +7,8 @@ using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders; using Content.Server.GameObjects.EntitySystems.JobQueues; -using Content.Server.Utility; using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Utility; using Robust.Server.Interfaces.Timing; using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Systems; @@ -281,7 +281,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering if (targetDistance <= steeringRequest.ArrivalDistance && steeringRequest.TimeUntilInteractionCheck <= 0.0f) { if (!steeringRequest.RequiresInRangeUnobstructed || - InteractionChecks.InRangeUnobstructed(entity, steeringRequest.TargetMap, steeringRequest.ArrivalDistance, ignoredEnt: entity)) + entity.InRangeUnobstructed(steeringRequest.TargetMap, steeringRequest.ArrivalDistance, popup: true)) { // TODO: Need cruder LOS checks for ranged weaps controller.VelocityDir = Vector2.Zero; diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index c46ffa7bb7..ddab046408 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -12,8 +12,8 @@ using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Input; using Content.Shared.Interfaces.GameObjects.Components; -using Content.Shared.Physics; using Content.Shared.Physics.Pull; +using Content.Shared.Utility; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.Interfaces.Player; @@ -71,7 +71,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click var interactionArgs = new DragDropEventArgs(performer, msg.DropLocation, dropped, target); // must be in range of both the target and the object they are drag / dropping - if (!InteractionChecks.InRangeUnobstructed(interactionArgs)) return; + if (!interactionArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; // trigger dragdrops on the dropped entity foreach (var dragDrop in dropped.GetAllComponents()) @@ -145,7 +145,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click // all activates should only fire when in range / unbostructed var activateEventArgs = new ActivateEventArgs {User = user, Target = used}; - if (InteractionChecks.InRangeUnobstructed(activateEventArgs)) + if (activateEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { activateComp.Activate(activateEventArgs); } @@ -453,7 +453,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click }; // all AttackBys should only happen when in range / unobstructed, so no range check is needed - if (InteractionChecks.InRangeUnobstructed(attackByEventArgs)) + if (attackByEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { foreach (var attackBy in attackBys) { @@ -501,8 +501,8 @@ namespace Content.Server.GameObjects.EntitySystems.Click var attackHands = attacked.GetAllComponents().ToList(); var attackHandEventArgs = new InteractHandEventArgs {User = user, Target = attacked}; - // all attackHands should only fire when in range / unbostructed - if (InteractionChecks.InRangeUnobstructed(attackHandEventArgs)) + // all attackHands should only fire when in range / unobstructed + if (attackHandEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { foreach (var attackHand in attackHands) { diff --git a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs index e766994372..557a6e8bf7 100644 --- a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs @@ -6,19 +6,18 @@ using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Stack; -using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.Utility; using Content.Shared.Construction; using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; -using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.Interfaces.Map; @@ -91,8 +90,7 @@ namespace Content.Server.GameObjects.EntitySystems if(targetEnt is null || handEnt is null) return; - var interaction = Get(); - if(!interaction.InRangeUnobstructed(handEnt.Transform.MapPosition, targetEnt.Transform.MapPosition, ignoredEnt: targetEnt, ignoreInsideBlocker: true)) + if (!handEnt.InRangeUnobstructed(targetEnt, ignoreInsideBlocker: true)) return; // Cannot deconstruct an entity with no prototype. @@ -246,8 +244,7 @@ namespace Content.Server.GameObjects.EntitySystems { var prototype = _prototypeManager.Index(prototypeName); - if (!InteractionChecks.InRangeUnobstructed(placingEnt, loc.ToMap(_mapManager), - ignoredEnt: placingEnt, ignoreInsideBlocker: prototype.CanBuildInImpassable)) + if (!placingEnt.InRangeUnobstructed(loc, ignoreInsideBlocker: prototype.CanBuildInImpassable, popup: true)) { return false; } @@ -377,7 +374,7 @@ namespace Content.Server.GameObjects.EntitySystems var constructPrototype = constructionComponent.Prototype; if (constructPrototype.CanBuildInImpassable == false) { - if (!InteractionChecks.InRangeUnobstructed(user, constructEntity.Transform.MapPosition)) + if (!user.InRangeUnobstructed(constructEntity, popup: true)) return false; } diff --git a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs index 1f46dbd7ee..efc7d8d797 100644 --- a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs +++ b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs @@ -3,6 +3,7 @@ using System; using System.Threading; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Physics; +using Content.Shared.Utility; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; @@ -13,17 +14,17 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter public sealed class DoAfterEventArgs { // Premade checks - public Func GetInRangeUnobstructed(int collisionMask = (int) CollisionGroup.MobMask) + public Func GetInRangeUnobstructed(CollisionGroup collisionMask = CollisionGroup.MobMask) { if (Target == null) { throw new InvalidOperationException("Can't supply a null target to DoAfterEventArgs.GetInRangeUnobstructed"); } - var interactionSystem = EntitySystem.Get(); - Func ignored = entity => entity == User || entity == Target; - return () => interactionSystem.InRangeUnobstructed(User.Transform.MapPosition, Target.Transform.MapPosition, collisionMask: collisionMask, predicate: ignored); + + bool Ignored(IEntity entity) => entity == User || entity == Target; + return () => User.InRangeUnobstructed(Target, collisionMask: collisionMask, predicate: Ignored); } - + /// /// The entity invoking do_after /// @@ -71,7 +72,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter /// Anything that needs a pre-check should do it itself so no DoAfterState is ever sent to the client. /// public Func? PostCheck { get; set; } = null; - + /// /// Additional conditions that need to be met. Return false to cancel. /// diff --git a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs index 68d95e7028..b5d521bac7 100644 --- a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs @@ -125,7 +125,7 @@ namespace Content.Server.GameObjects.EntitySystems var entToDesiredDropCoords = coords.Position - entCoords; var targetLength = Math.Min(entToDesiredDropCoords.Length, SharedInteractionSystem.InteractionRange - 0.001f); // InteractionRange is reduced due to InRange not dealing with floating point error var newCoords = new GridCoordinates((entToDesiredDropCoords.Normalized * targetLength) + entCoords, coords.GridID); - var rayLength = Get().UnobstructedRayLength(ent.Transform.MapPosition, newCoords.ToMap(_mapManager), ignoredEnt: ent); + var rayLength = Get().UnobstructedDistance(ent.Transform.MapPosition, newCoords.ToMap(_mapManager), ignoredEnt: ent); handsComp.Drop(handsComp.ActiveHand, new GridCoordinates(entCoords + (entToDesiredDropCoords.Normalized * rayLength), coords.GridID)); diff --git a/Content.Server/Utility/InteractionChecks.cs b/Content.Server/Utility/InteractionChecks.cs deleted file mode 100644 index 1f5a452b74..0000000000 --- a/Content.Server/Utility/InteractionChecks.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Content.Shared.GameObjects.EntitySystems; -using Content.Shared.Interfaces; -using Content.Shared.Interfaces.GameObjects.Components; -using Content.Shared.Physics; -using Robust.Shared.GameObjects.Systems; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Interfaces.Map; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Map; - -namespace Content.Server.Utility -{ - /// - /// Convenient methods for checking for various conditions commonly needed - /// for interactions. - /// - public static class InteractionChecks - { - - /// - /// Default interaction check for targeted attack interaction types. - /// Same as , but defaults to ignore inside blockers - /// (making the check less restrictive). - /// Validates that attacker is in range of the attacked entity. Additionally shows a popup if - /// validation fails. - /// - public static bool InRangeUnobstructed(ITargetedInteractEventArgs eventArgs, bool ignoreInsideBlocker = true) - { - if (!EntitySystem.Get().InRangeUnobstructed(eventArgs.User.Transform.MapPosition, - eventArgs.Target.Transform.MapPosition, ignoredEnt: eventArgs.Target, ignoreInsideBlocker: ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - eventArgs.Target.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!")); - return false; - } - - return true; - } - - /// - /// Default interaction check for Drag drop interaction types. - /// Same as , but defaults to ignore inside blockers - /// (making the check less restrictive) and checks reachability of both the target and the dragged / dropped object. - /// Additionally shows a popup if validation fails. - /// - public static bool InRangeUnobstructed(DragDropEventArgs eventArgs, bool ignoreInsideBlocker = true) - { - if (!EntitySystem.Get().InRangeUnobstructed(eventArgs.User.Transform.MapPosition, - eventArgs.Target.Transform.MapPosition, ignoredEnt: eventArgs.Target, ignoreInsideBlocker: ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - eventArgs.Target.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!")); - return false; - } - if (!EntitySystem.Get().InRangeUnobstructed(eventArgs.User.Transform.MapPosition, - eventArgs.Dropped.Transform.MapPosition, ignoredEnt: eventArgs.Dropped, ignoreInsideBlocker: ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - eventArgs.Dropped.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!")); - return false; - } - - return true; - } - - /// - /// Default interaction check for after attack interaction types. - /// Same as , but defaults to ignore inside blockers - /// (making the check less restrictive). - /// Validates that attacker is in range of the attacked entity, if there is such an entity. - /// If there is no attacked entity, validates that they are in range of the clicked position. - /// Additionally shows a popup if validation fails. - /// - public static bool InRangeUnobstructed(AfterInteractEventArgs eventArgs, bool ignoreInsideBlocker = true) - { - if (eventArgs.Target != null) - { - if (!EntitySystem.Get().InRangeUnobstructed(eventArgs.User.Transform.MapPosition, - eventArgs.Target.Transform.MapPosition, ignoredEnt: eventArgs.Target, ignoreInsideBlocker: ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - eventArgs.Target.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!")); - return false; - } - } - else - { - if (!EntitySystem.Get().InRangeUnobstructed(eventArgs.User.Transform.MapPosition, - eventArgs.ClickLocation.ToMap(IoCManager.Resolve()), ignoredEnt: eventArgs.User, ignoreInsideBlocker: ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - eventArgs.User.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!")); - return false; - } - } - - - return true; - } - - /// - /// Convenient static alternative to , which also - /// shows a popup message if not in range. - /// - public static bool InRangeUnobstructed(IEntity user, MapCoordinates otherCoords, - float range = SharedInteractionSystem.InteractionRange, - int collisionMask = (int) CollisionGroup.Impassable, IEntity ignoredEnt = null, bool ignoreInsideBlocker = false) - { - var mapManager = IoCManager.Resolve(); - var interactionSystem = EntitySystem.Get(); - if (!interactionSystem.InRangeUnobstructed(user.Transform.MapPosition, otherCoords, range, collisionMask, - ignoredEnt, ignoreInsideBlocker)) - { - var localizationManager = IoCManager.Resolve(); - user.PopupMessage(user, localizationManager.GetString("You can't reach there!")); - - return false; - } - - return true; - } - } -} diff --git a/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs b/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs index 71e0190d8d..d8eaea3172 100644 --- a/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs +++ b/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs @@ -1,11 +1,13 @@ using System; using Content.Shared.GameObjects.Components.Mobs; +using Content.Shared.Utility; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Maths; using Robust.Shared.Utility; +using static Content.Shared.GameObjects.EntitySystems.SharedInteractionSystem; namespace Content.Shared.GameObjects.EntitySystems { @@ -26,11 +28,8 @@ namespace Content.Shared.GameObjects.EntitySystems private static bool IsInDetailsRange(IEntity examiner, IEntity entity) { - return Get() - .InRangeUnobstructed(examiner.Transform.MapPosition, entity.Transform.MapPosition, - ExamineDetailsRange, predicate: entity0 => entity0 == examiner || entity0 == entity, - ignoreInsideBlocker: true) && - examiner.IsInSameOrNoContainer(entity); + return examiner.InRangeUnobstructed(entity, ExamineDetailsRange, ignoreInsideBlocker: true) && + examiner.IsInSameOrNoContainer(entity); } [Pure] @@ -51,16 +50,14 @@ namespace Content.Shared.GameObjects.EntitySystems return false; } - Func predicate = entity => entity == examiner || entity == examined; + Ignored predicate = entity => entity == examiner || entity == examined; if (ContainerHelpers.TryGetContainer(examiner, out var container)) { predicate += entity => entity == container.Owner; } - return Get() - .InRangeUnobstructed(examiner.Transform.MapPosition, examined.Transform.MapPosition, - ExamineRange, predicate: predicate, ignoreInsideBlocker:true); + return examiner.InRangeUnobstructed(examined, ExamineRange, predicate: predicate, ignoreInsideBlocker: true); } public static FormattedMessage GetExamineText(IEntity entity, IEntity examiner) diff --git a/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs b/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs index 610b414c65..e795676e78 100644 --- a/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs @@ -1,11 +1,14 @@ -using System; -using System.Linq; +using System.Linq; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Physics; using JetBrains.Annotations; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Physics; using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -18,98 +21,516 @@ namespace Content.Shared.GameObjects.EntitySystems public class SharedInteractionSystem : EntitySystem { [Dependency] private readonly IPhysicsManager _physicsManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; public const float InteractionRange = 2; public const float InteractionRangeSquared = InteractionRange * InteractionRange; + public delegate bool Ignored(IEntity entity); + /// /// Traces a ray from coords to otherCoords and returns the length /// of the vector between coords and the ray's first hit. /// - /// Set of coordinates to use. - /// Other set of coordinates to use. + /// Set of coordinates to use. + /// Other set of coordinates to use. /// the mask to check for collisions - /// A predicate to check whether to ignore an entity or not. If it returns true, it will be ignored. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// /// Length of resulting ray. - public float UnobstructedRayLength(MapCoordinates coords, MapCoordinates otherCoords, - int collisionMask = (int) CollisionGroup.Impassable, Func predicate = null) + public float UnobstructedDistance( + MapCoordinates origin, + MapCoordinates other, + int collisionMask = (int) CollisionGroup.Impassable, + Ignored predicate = null) { - var dir = otherCoords.Position - coords.Position; + var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) return 0f; - var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); - var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, returnOnFirstHit: false).ToList(); + predicate ??= _ => false; + var ray = new CollisionRay(origin.Position, dir.Normalized, collisionMask); + var rayResults = _physicsManager.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); if (rayResults.Count == 0) return dir.Length; - return (rayResults[0].HitPos - coords.Position).Length; + return (rayResults[0].HitPos - origin.Position).Length; } /// /// Traces a ray from coords to otherCoords and returns the length /// of the vector between coords and the ray's first hit. /// - /// Set of coordinates to use. - /// Other set of coordinates to use. - /// the mask to check for collisions - /// the entity to be ignored when checking for collisions. + /// Set of coordinates to use. + /// Other set of coordinates to use. + /// The mask to check for collisions + /// + /// The entity to be ignored when checking for collisions. + /// /// Length of resulting ray. - public float UnobstructedRayLength(MapCoordinates coords, MapCoordinates otherCoords, - int collisionMask = (int) CollisionGroup.Impassable, IEntity ignoredEnt = null) => - UnobstructedRayLength(coords, otherCoords, collisionMask, - ignoredEnt == null ? null : (Func) (entity => ignoredEnt == entity)); + public float UnobstructedDistance( + MapCoordinates origin, + MapCoordinates other, + int collisionMask = (int) CollisionGroup.Impassable, + IEntity ignoredEnt = null) + { + var predicate = ignoredEnt == null + ? null + : (Ignored) (e => e == ignoredEnt); + + return UnobstructedDistance(origin, other, collisionMask, predicate); + } /// /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the is zero or negative, - /// this method will only check if nothing obstructs the two sets of coordinates.. + /// this method will only check if nothing obstructs the two sets + /// of coordinates. /// - /// Set of coordinates to use. - /// Other set of coordinates to use. - /// maximum distance between the two sets of coordinates. - /// the mask to check for collisions - /// A predicate to check whether to ignore an entity or not. If it returns true, it will be ignored. - /// if true and the coordinates are inside the obstruction, ignores the obstruction and - /// considers the interaction unobstructed. Therefore, setting this to true makes this check more permissive, such - /// as allowing an interaction to occur inside something impassable (like a wall). The default, false, - /// makes the check more restrictive. - /// True if the two points are within a given range without being obstructed. - public bool InRangeUnobstructed(MapCoordinates coords, MapCoordinates otherCoords, float range = InteractionRange, - int collisionMask = (int)CollisionGroup.Impassable, Func predicate = null, bool ignoreInsideBlocker = false) + /// Set of coordinates to use. + /// Other set of coordinates to use. + /// + /// Maximum distance between the two sets of coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and or are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + MapCoordinates origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) { - if (!coords.InRange(otherCoords, range)) - return false; + if (!origin.InRange(other, range)) return false; - var dir = otherCoords.Position - coords.Position; + var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) return true; if (range > 0f && !(dir.LengthSquared <= range * range)) return false; - var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); - var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, returnOnFirstHit: false).ToList(); - return rayResults.Count == 0 || (ignoreInsideBlocker && rayResults.Count > 0 && (rayResults[0].HitPos - otherCoords.Position).Length < 1f); + predicate ??= _ => false; + + var ray = new CollisionRay(origin.Position, dir.Normalized, (int) collisionMask); + var rayResults = _physicsManager.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); + + if (rayResults.Count == 0) return true; + + if (!ignoreInsideBlocker) return false; + + if (rayResults.Count <= 0) return false; + + return (rayResults[0].HitPos - other.Position).Length < 1f; } /// - /// Checks that these coordinates are within a certain distance without any + /// Checks that two entities are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the is zero or negative, - /// this method will only check if nothing obstructs the two sets of coordinates.. + /// this method will only check if nothing obstructs the two entities. /// - /// Set of coordinates to use. - /// Other set of coordinates to use. - /// maximum distance between the two sets of coordinates. - /// the mask to check for collisions - /// the entity to be ignored when checking for collisions. - /// if true and the coordinates are inside the obstruction, ignores the obstruction and - /// considers the interaction unobstructed. Therefore, setting this to true makes this check more permissive, such - /// as allowing an interaction to occur inside something impassable (like a wall). The default, false, - /// makes the check more restrictive. - /// True if the two points are within a given range without being obstructed. - public bool InRangeUnobstructed(MapCoordinates coords, MapCoordinates otherCoords, float range = InteractionRange, - int collisionMask = (int)CollisionGroup.Impassable, IEntity ignoredEnt = null, bool ignoreInsideBlocker = false) => - InRangeUnobstructed(coords, otherCoords, range, collisionMask, - ignoredEnt == null ? null : (Func)(entity => ignoredEnt == entity), ignoreInsideBlocker); + /// The first entity to use. + /// Other entity to use. + /// + /// Maximum distance between the two entities. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and or are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the origin entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + IEntity origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originPosition = origin.Transform.MapPosition; + var otherPosition = other.Transform.MapPosition; + predicate ??= e => e == origin || e == other; + + var inRange = InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, predicate, ignoreInsideBlocker); + + if (!inRange && popup) + { + var message = Loc.GetString("You can't reach there!"); + origin.PopupMessage(origin, message); + } + + return inRange; + } + + /// + /// Checks that an entity and a component are within a certain + /// distance without any entity that matches the collision mask + /// obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The entity to use. + /// The component to use. + /// + /// Maximum distance between the entity and component. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and or are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the origin entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + IEntity origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originPosition = origin.Transform.MapPosition; + var otherPosition = other.Owner.Transform.MapPosition; + predicate ??= e => e == origin || e == other.Owner; + + var inRange = InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, predicate, ignoreInsideBlocker); + + if (!inRange && popup) + { + var message = Loc.GetString("You can't reach there!"); + origin.PopupMessage(origin, message); + } + + return inRange; + } + + /// + /// Checks that an entity and a set of grid coordinates are within a certain + /// distance without any entity that matches the collision mask + /// obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The entity to use. + /// The grid coordinates to use. + /// + /// Maximum distance between the two entity and set of grid coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and or are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the origin entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + IEntity origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originPosition = origin.Transform.MapPosition; + var otherPosition = other.ToMap(_mapManager); + predicate ??= e => e == origin; + + var inRange = InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, predicate, ignoreInsideBlocker); + + if (!inRange && popup) + { + var message = Loc.GetString("You can't reach there!"); + origin.PopupMessage(origin, message); + } + + return inRange; + } + + /// + /// Checks that an entity and a set of map coordinates are within a certain + /// distance without any entity that matches the collision mask + /// obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The entity to use. + /// The map coordinates to use. + /// + /// Maximum distance between the two entity and set of map coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and or are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the origin entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + IEntity origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originPosition = origin.Transform.MapPosition; + predicate ??= e => e == origin; + + var inRange = InRangeUnobstructed(originPosition, other, range, collisionMask, predicate, ignoreInsideBlocker); + + if (!inRange && popup) + { + var message = Loc.GetString("You can't reach there!"); + origin.PopupMessage(origin, message); + } + + return inRange; + } + + /// + /// Checks that the user and target of a + /// are within a certain + /// distance without any entity that matches the collision mask + /// obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The event args to use. + /// + /// Maximum distance between the two entity and set of map coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and both the user and target are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the user entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + ITargetedInteractEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var origin = args.User; + var other = args.Target; + + return InRangeUnobstructed(origin, other, range, collisionMask, predicate, ignoreInsideBlocker, popup); + } + + /// + /// Checks that the user of a is within a + /// certain distance of the target and dropped entities without any entity + /// that matches the collision mask obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The event args to use. + /// + /// Maximum distance between the two entity and set of map coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and both the user and target are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the user entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + DragDropEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var user = args.User; + var dropped = args.Dropped; + var target = args.Target; + + if (!InRangeUnobstructed(user, target, range, collisionMask, predicate, ignoreInsideBlocker)) + { + if (popup) + { + var message = Loc.GetString("You can't reach there!"); + target.PopupMessage(user, message); + } + + return false; + } + + if (!InRangeUnobstructed(user, dropped, range, collisionMask, predicate, ignoreInsideBlocker)) + { + if (popup) + { + var message = Loc.GetString("You can't reach there!"); + dropped.PopupMessage(user, message); + } + + return false; + } + + return true; + } + + /// + /// Checks that the user and target of a + /// are within a certain distance + /// without any entity that matches the collision mask obstructing them. + /// If the is zero or negative, + /// this method will only check if nothing obstructs the entity and component. + /// + /// The event args to use. + /// + /// Maximum distance between the two entity and set of map coordinates. + /// + /// The mask to check for collisions. + /// + /// A predicate to check whether to ignore an entity or not. + /// If it returns true, it will be ignored. + /// + /// + /// If true and both the user and target are inside + /// the obstruction, ignores the obstruction and considers the interaction + /// unobstructed. + /// Therefore, setting this to true makes this check more permissive, + /// such as allowing an interaction to occur inside something impassable + /// (like a wall). The default, false, makes the check more restrictive. + /// + /// + /// Whether or not to popup a feedback message on the user entity for + /// it to see. + /// + /// + /// True if the two points are within a given range without being obstructed. + /// + public bool InRangeUnobstructed( + AfterInteractEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var user = args.User; + var target = args.Target; + predicate ??= e => e == user; + + MapCoordinates otherPosition; + + if (target == null) + { + otherPosition = args.ClickLocation.ToMap(_mapManager); + } + else + { + otherPosition = target.Transform.MapPosition; + predicate += e => e == target; + } + + return InRangeUnobstructed(user, otherPosition, range, collisionMask, predicate, ignoreInsideBlocker, popup); + } } } diff --git a/Content.Shared/GameObjects/Verbs/SharedVerbSystem.cs b/Content.Shared/GameObjects/Verbs/SharedVerbSystem.cs index 51714a1198..82c5a940e2 100644 --- a/Content.Shared/GameObjects/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/GameObjects/Verbs/SharedVerbSystem.cs @@ -1,10 +1,9 @@ #nullable enable -using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Physics; +using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; @@ -15,14 +14,6 @@ namespace Content.Shared.GameObjects.Verbs { public class SharedVerbSystem : EntitySystem { - private SharedInteractionSystem _interactionSystem = null!; - - public override void Initialize() - { - base.Initialize(); - _interactionSystem = Get(); - } - /// /// Get all of the entities relevant for the contextmenu /// @@ -35,29 +26,27 @@ namespace Content.Shared.GameObjects.Verbs { contextEntities = null; var length = buffer ? 1.0f: 0.5f; - + var entities = EntityManager.GetEntitiesIntersecting(targetPos.MapId, Box2.CenteredAround(targetPos.Position, (length, length))).ToList(); - + if (entities.Count == 0) { return false; } - + // Check if we have LOS to the clicked-location, otherwise no popup. var vectorDiff = player.Transform.MapPosition.Position - targetPos.Position; var distance = vectorDiff.Length + 0.01f; - Func ignored = entity => entities.Contains(entity) || - entity == player || - !entity.TryGetComponent(out OccluderComponent? occluder) || - !occluder.Enabled; + bool Ignored(IEntity entity) + { + return entities.Contains(entity) || + entity == player || + !entity.TryGetComponent(out OccluderComponent? occluder) || + !occluder.Enabled; + } - var result = _interactionSystem.InRangeUnobstructed( - player.Transform.MapPosition, - targetPos, - distance, - (int) CollisionGroup.Opaque, - ignored); + var result = player.InRangeUnobstructed(targetPos, distance, CollisionGroup.Opaque, Ignored); if (!result) { @@ -68,4 +57,4 @@ namespace Content.Shared.GameObjects.Verbs return true; } } -} \ No newline at end of file +} diff --git a/Content.Shared/Utility/SharedRangeExtensions.cs b/Content.Shared/Utility/SharedRangeExtensions.cs new file mode 100644 index 0000000000..326d2adfeb --- /dev/null +++ b/Content.Shared/Utility/SharedRangeExtensions.cs @@ -0,0 +1,432 @@ +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Physics; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.Shared.GameObjects.EntitySystems.SharedInteractionSystem; + +namespace Content.Shared.Utility +{ + public static class SharedRangeExtensions + { + private static SharedInteractionSystem SharedInteractionSystem => EntitySystem.Get(); + + #region Entities + public static bool InRangeUnobstructed( + this IEntity origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(origin, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IEntity origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(origin, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IEntity origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var otherEntity = other.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(origin, otherEntity, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IEntity origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(origin, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IEntity origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(origin, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + #endregion + + #region Components + public static bool InRangeUnobstructed( + this IComponent origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IComponent origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IComponent origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + var otherEntity = other.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, otherEntity, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IComponent origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IComponent origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + #endregion + + #region Containers + public static bool InRangeUnobstructed( + this IContainer origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this IContainer origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IContainer origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + var otherEntity = other.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, otherEntity, range, collisionMask, + predicate, ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IContainer origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this IContainer origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + var originEntity = origin.Owner; + + return SharedInteractionSystem.InRangeUnobstructed(originEntity, other, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + #endregion + + #region GridCoordinates + public static bool InRangeUnobstructed( + this GridCoordinates origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var originPosition = origin.ToMap(mapManager); + var otherPosition = other.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, + predicate, ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this GridCoordinates origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var originPosition = origin.ToMap(mapManager); + var otherPosition = other.Owner.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, + predicate, ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this GridCoordinates origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var originPosition = origin.ToMap(mapManager); + var otherPosition = other.Owner.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, + predicate, ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this GridCoordinates origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var originPosition = origin.ToMap(mapManager); + var otherPosition = other.ToMap(mapManager); + + return SharedInteractionSystem.InRangeUnobstructed(originPosition, otherPosition, range, collisionMask, + predicate, ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this GridCoordinates origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var originPosition = origin.ToMap(mapManager); + + return SharedInteractionSystem.InRangeUnobstructed(originPosition, other, range, collisionMask, predicate, + ignoreInsideBlocker); + } + #endregion + + #region MapCoordinates + public static bool InRangeUnobstructed( + this MapCoordinates origin, + IEntity other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var otherPosition = other.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(origin, otherPosition, range, collisionMask, predicate, + ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this MapCoordinates origin, + IComponent other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var otherPosition = other.Owner.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(origin, otherPosition, range, collisionMask, predicate, + ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this MapCoordinates origin, + IContainer other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var otherPosition = other.Owner.Transform.MapPosition; + + return SharedInteractionSystem.InRangeUnobstructed(origin, otherPosition, range, collisionMask, predicate, + ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this MapCoordinates origin, + GridCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + var mapManager = IoCManager.Resolve(); + var otherPosition = other.ToMap(mapManager); + + return SharedInteractionSystem.InRangeUnobstructed(origin, otherPosition, range, collisionMask, predicate, + ignoreInsideBlocker); + } + + public static bool InRangeUnobstructed( + this MapCoordinates origin, + MapCoordinates other, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false) + { + return SharedInteractionSystem.InRangeUnobstructed(origin, other, range, collisionMask, predicate, + ignoreInsideBlocker); + } + #endregion + + #region EventArgs + public static bool InRangeUnobstructed( + this ITargetedInteractEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(args, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this DragDropEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(args, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + + public static bool InRangeUnobstructed( + this AfterInteractEventArgs args, + float range = InteractionRange, + CollisionGroup collisionMask = CollisionGroup.Impassable, + Ignored predicate = null, + bool ignoreInsideBlocker = false, + bool popup = false) + { + return SharedInteractionSystem.InRangeUnobstructed(args, range, collisionMask, predicate, + ignoreInsideBlocker, popup); + } + #endregion + } +}