using System; using System.Linq; 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.Map; using Robust.Shared.Maths; namespace Content.Server.GameObjects.EntitySystems { /// /// Governs interactions during clicking on entities /// [UsedImplicitly] public class SharedInteractionSystem : EntitySystem { #pragma warning disable 649 [Dependency] private readonly IPhysicsManager _physicsManager; #pragma warning restore 649 public const float InteractionRange = 2; public const float InteractionRangeSquared = InteractionRange * InteractionRange; /// /// 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 /// 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) { var dir = otherCoords.Position - coords.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(); if (rayResults.Count == 0) return dir.Length; return (rayResults[0].HitPos - coords.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. /// 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)); /// /// 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.. /// /// 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 coordinates inside obstructions count as obstructed or not /// 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 insideBlockerValid = false) { if (!coords.InRange(otherCoords, range)) return false; var dir = otherCoords.Position - coords.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 || (insideBlockerValid && rayResults.Count > 0 && (rayResults[0].HitPos - otherCoords.Position).Length < 1f); } /// /// 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.. /// /// 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 coordinates inside obstructions count as obstructed or not /// 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 insideBlockerValid = false) => InRangeUnobstructed(coords, otherCoords, range, collisionMask, ignoredEnt == null ? null : (Func)(entity => ignoredEnt == entity), insideBlockerValid); } }