* Content side new physics structure * BroadPhase outline done * But we need to fix WorldAABB * Fix static pvs AABB * Fix import * Rando fixes * B is for balloon * Change human mob hitbox to circle * Decent movement * Start adding friction to player controller I think it's the best way to go about it to keep other objects somewhat consistent for physics. * This baby can fit so many physics bugs in it. * Slight mob mover optimisations. * Player mover kinda works okay. * Beginnings of testbed * More testbed * Circlestack bed * Namespaces * BB fixes * Pull WorldAABB * Joint pulling * Semi-decent movement I guess. * Pulling better * Bullet controller + old movement * im too dumb for this shit * Use kinematic mob controller again It's probably for the best TBH * Stashed shitcode * Remove SlipController * In which movement code is entirely refactored * Singularity fix * Fix ApplyLinearImpulse * MoveRelay fix * Fix door collisions * Disable subfloor collisions Saves on broadphase a fair bit * Re-implement ClimbController * Zumzum's pressure * Laggy item throwing * Minor atmos change * Some caching * Optimise controllers * Optimise CollideWith to hell and back * Re-do throwing and tile friction * Landing too * Optimise controllers * Move CCVars and other stuff swept is beautiful * Cleanup a bunch of controllers * Fix shooting and high pressure movement controller * Flashing improvements * Stuff and things * Combat collisions * Combat mode collisions * Pulling distance joint again * Cleanup physics interfaces * More like scuffedularity * Shit's fucked * Haha tests go green * Bigmoneycrab * Fix dupe pulling * Zumzum's based fix * Don't run tile friction for non-predicted bodies * Experimental pulling improvement * Everything's a poly now * Optimise AI region debugging a bit Could still be better but should improve default performance a LOT * Mover no updater * Crazy kinematic body idea * Good collisions * KinematicController * Fix aghost * Throwing refactor * Pushing cleanup * Fix throwing and footstep sounds * Frametime in ICollideBehavior * Fix stuff * Actually fix weightlessness * Optimise collision behaviors a lot * Make open lockers still collide with walls * powwweeerrrrr * Merge master proper * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * Ch ch ch changesss * SHIP IT * Fix #if DEBUG * Fix vaulting and item locker collision * Fix throwing * Editing yaml by hand what can go wrong * on * Last yaml fixes * Okay now it's fixed * Linter Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com> Co-authored-by: Vera Aguilera Puerto <zddm@outlook.es>
548 lines
24 KiB
C#
548 lines
24 KiB
C#
using System.Linq;
|
|
using Content.Shared.Interfaces;
|
|
using Content.Shared.Interfaces.GameObjects.Components;
|
|
using Content.Shared.Physics;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Localization;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Broadphase;
|
|
|
|
namespace Content.Shared.GameObjects.EntitySystems
|
|
{
|
|
/// <summary>
|
|
/// Governs interactions during clicking on entities
|
|
/// </summary>
|
|
[UsedImplicitly]
|
|
public class SharedInteractionSystem : EntitySystem
|
|
{
|
|
public const float InteractionRange = 2;
|
|
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
|
|
|
|
public delegate bool Ignored(IEntity entity);
|
|
|
|
/// <summary>
|
|
/// Traces a ray from coords to otherCoords and returns the length
|
|
/// of the vector between coords and the ray's first hit.
|
|
/// </summary>
|
|
/// <param name="origin">Set of coordinates to use.</param>
|
|
/// <param name="other">Other set of coordinates to use.</param>
|
|
/// <param name="collisionMask">the mask to check for collisions</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <returns>Length of resulting ray.</returns>
|
|
public float UnobstructedDistance(
|
|
MapCoordinates origin,
|
|
MapCoordinates other,
|
|
int collisionMask = (int) CollisionGroup.Impassable,
|
|
Ignored predicate = null)
|
|
{
|
|
var dir = other.Position - origin.Position;
|
|
|
|
if (dir.LengthSquared.Equals(0f)) return 0f;
|
|
|
|
predicate ??= _ => false;
|
|
var ray = new CollisionRay(origin.Position, dir.Normalized, collisionMask);
|
|
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
|
|
|
|
if (rayResults.Count == 0) return dir.Length;
|
|
return (rayResults[0].HitPos - origin.Position).Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traces a ray from coords to otherCoords and returns the length
|
|
/// of the vector between coords and the ray's first hit.
|
|
/// </summary>
|
|
/// <param name="origin">Set of coordinates to use.</param>
|
|
/// <param name="other">Other set of coordinates to use.</param>
|
|
/// <param name="collisionMask">The mask to check for collisions</param>
|
|
/// <param name="ignoredEnt">
|
|
/// The entity to be ignored when checking for collisions.
|
|
/// </param>
|
|
/// <returns>Length of resulting ray.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that these coordinates are within a certain distance without any
|
|
/// entity that matches the collision mask obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the two sets
|
|
/// of coordinates.
|
|
/// </summary>
|
|
/// <param name="origin">Set of coordinates to use.</param>
|
|
/// <param name="other">Other set of coordinates to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two sets of coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// If true and <see cref="origin"/> or <see cref="other"/> 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.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
public bool InRangeUnobstructed(
|
|
MapCoordinates origin,
|
|
MapCoordinates other,
|
|
float range = InteractionRange,
|
|
CollisionGroup collisionMask = CollisionGroup.Impassable,
|
|
Ignored predicate = null,
|
|
bool ignoreInsideBlocker = false)
|
|
{
|
|
if (!origin.InRange(other, range)) return false;
|
|
|
|
var dir = other.Position - origin.Position;
|
|
|
|
if (dir.LengthSquared.Equals(0f)) return true;
|
|
if (range > 0f && !(dir.LengthSquared <= range * range)) return false;
|
|
|
|
predicate ??= _ => false;
|
|
|
|
var ray = new CollisionRay(origin.Position, dir.Normalized, (int) collisionMask);
|
|
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
|
|
|
|
if (rayResults.Count == 0) return true;
|
|
|
|
if (!ignoreInsideBlocker) return false;
|
|
|
|
foreach (var result in rayResults)
|
|
{
|
|
if (!result.HitEntity.TryGetComponent(out IPhysBody p))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var bBox = p.GetWorldAABB();
|
|
|
|
if (bBox.Contains(origin.Position) || bBox.Contains(other.Position))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that two entities are within a certain distance without any
|
|
/// entity that matches the collision mask obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the two entities.
|
|
/// </summary>
|
|
/// <param name="origin">The first entity to use.</param>
|
|
/// <param name="other">Other entity to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entities.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// If true and <see cref="origin"/> or <see cref="other"/> 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the origin entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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(message);
|
|
}
|
|
|
|
return inRange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that an entity and a component are within a certain
|
|
/// distance without any entity that matches the collision mask
|
|
/// obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="origin">The entity to use.</param>
|
|
/// <param name="other">The component to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the entity and component.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// If true and <see cref="origin"/> or <see cref="other"/> 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the origin entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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(message);
|
|
}
|
|
|
|
return inRange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="origin">The entity to use.</param>
|
|
/// <param name="other">The grid coordinates to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entity and set of grid coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// If true and <see cref="origin"/> or <see cref="other"/> 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the origin entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
public bool InRangeUnobstructed(
|
|
IEntity origin,
|
|
EntityCoordinates 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(EntityManager);
|
|
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(message);
|
|
}
|
|
|
|
return inRange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="origin">The entity to use.</param>
|
|
/// <param name="other">The map coordinates to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entity and set of map coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// If true and <see cref="origin"/> or <see cref="other"/> 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the origin entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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(message);
|
|
}
|
|
|
|
return inRange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that the user and target of a
|
|
/// <see cref="ITargetedInteractEventArgs"/> are within a certain
|
|
/// distance without any entity that matches the collision mask
|
|
/// obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="args">The event args to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entity and set of map coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the user entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that the user of a <see cref="DragDropEventArgs"/> is within a
|
|
/// certain distance of the target and dropped entities without any entity
|
|
/// that matches the collision mask obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="args">The event args to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entity and set of map coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the user entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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.Dragged;
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks that the user and target of a
|
|
/// <see cref="AfterInteractEventArgs"/> are within a certain distance
|
|
/// without any entity that matches the collision mask obstructing them.
|
|
/// If the <paramref name="range"/> is zero or negative,
|
|
/// this method will only check if nothing obstructs the entity and component.
|
|
/// </summary>
|
|
/// <param name="args">The event args to use.</param>
|
|
/// <param name="range">
|
|
/// Maximum distance between the two entity and set of map coordinates.
|
|
/// </param>
|
|
/// <param name="collisionMask">The mask to check for collisions.</param>
|
|
/// <param name="predicate">
|
|
/// A predicate to check whether to ignore an entity or not.
|
|
/// If it returns true, it will be ignored.
|
|
/// </param>
|
|
/// <param name="ignoreInsideBlocker">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="popup">
|
|
/// Whether or not to popup a feedback message on the user entity for
|
|
/// it to see.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the two points are within a given range without being obstructed.
|
|
/// </returns>
|
|
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(EntityManager);
|
|
}
|
|
else
|
|
{
|
|
otherPosition = target.Transform.MapPosition;
|
|
predicate += e => e == target;
|
|
}
|
|
|
|
return InRangeUnobstructed(user, otherPosition, range, collisionMask, predicate, ignoreInsideBlocker, popup);
|
|
}
|
|
}
|
|
}
|