Files
tbd-station-14/Content.Server/Pinpointer/ServerPinpointerSystem.cs
2022-03-19 19:27:26 +01:00

168 lines
5.9 KiB
C#

using Content.Shared.Interaction;
using Content.Shared.Pinpointer;
using System.Linq;
using Robust.Shared.Utility;
namespace Content.Server.Pinpointer
{
public sealed class ServerPinpointerSystem : SharedPinpointerSystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PinpointerComponent, ActivateInWorldEvent>(OnActivate);
}
private void OnActivate(EntityUid uid, PinpointerComponent component, ActivateInWorldEvent args)
{
TogglePinpointer(uid, component);
// try to find target from whitelist
if (component.IsActive && component.Component != null)
{
if (!EntityManager.ComponentFactory.TryGetRegistration(component.Component, out var reg))
{
Logger.Error($"Unable to find component registration for {component.Component} for pinpointer!");
DebugTools.Assert(false);
return;
}
var target = FindTargetFromComponent(uid, reg.Type);
SetTarget(uid, target, component);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// because target or pinpointer can move
// we need to update pinpointers arrow each frame
foreach (var uid in ActivePinpointers)
{
UpdateDirectionToTarget(uid);
}
}
/// <summary>
/// Try to find the closest entity from whitelist on a current map
/// Will return null if can't find anything
/// </summary>
private EntityUid? FindTargetFromComponent(EntityUid uid, Type whitelist, TransformComponent? transform = null)
{
var xformQuery = GetEntityQuery<TransformComponent>();
if (transform == null)
xformQuery.TryGetComponent(uid, out transform);
if (transform == null)
return null;
// sort all entities in distance increasing order
var mapId = transform.MapID;
var l = new SortedList<float, EntityUid>();
var worldPos = _transform.GetWorldPosition(transform, xformQuery);
foreach (var comp in EntityManager.GetAllComponents(whitelist))
{
if (!xformQuery.TryGetComponent(comp.Owner, out var compXform) ||
compXform.MapID != mapId) continue;
var dist = (_transform.GetWorldPosition(compXform, xformQuery) - worldPos).LengthSquared;
l.TryAdd(dist, comp.Owner);
}
// return uid with a smallest distacne
return l.Count > 0 ? l.First().Value : null;
}
/// <summary>
/// Set pinpointers target to track
/// </summary>
public void SetTarget(EntityUid uid, EntityUid? target, PinpointerComponent? pinpointer = null)
{
if (!Resolve(uid, ref pinpointer))
return;
if (pinpointer.Target == target)
return;
pinpointer.Target = target;
if (pinpointer.IsActive)
UpdateDirectionToTarget(uid, pinpointer);
}
/// <summary>
/// Update direction from pinpointer to selected target (if it was set)
/// </summary>
private void UpdateDirectionToTarget(EntityUid uid, PinpointerComponent? pinpointer = null)
{
if (!Resolve(uid, ref pinpointer))
return;
var target = pinpointer.Target;
if (target == null || !EntityManager.EntityExists(target.Value))
{
SetDirection(uid, Direction.Invalid, pinpointer);
SetDistance(uid, Distance.UNKNOWN, pinpointer);
return;
}
var dirVec = CalculateDirection(uid, target.Value);
if (dirVec != null)
{
var dir = dirVec.Value.GetDir();
SetDirection(uid, dir, pinpointer);
var dist = CalculateDistance(uid, dirVec.Value, pinpointer);
SetDistance(uid, dist, pinpointer);
}
else
{
SetDirection(uid, Direction.Invalid, pinpointer);
SetDistance(uid, Distance.UNKNOWN, pinpointer);
}
}
/// <summary>
/// Calculate direction from pinUid to trgUid
/// </summary>
/// <returns>Null if failed to caluclate distance between two entities</returns>
private Vector2? CalculateDirection(EntityUid pinUid, EntityUid trgUid)
{
var xformQuery = GetEntityQuery<TransformComponent>();
// check if entities have transform component
if (!xformQuery.TryGetComponent(pinUid, out var pin))
return null;
if (!xformQuery.TryGetComponent(trgUid, out var trg))
return null;
// check if they are on same map
if (pin.MapID != trg.MapID)
return null;
// get world direction vector
var dir = (_transform.GetWorldPosition(trg, xformQuery) - _transform.GetWorldPosition(pin, xformQuery));
return dir;
}
private Distance CalculateDistance(EntityUid uid, Vector2 vec, PinpointerComponent? pinpointer = null)
{
if (!Resolve(uid, ref pinpointer))
return Distance.UNKNOWN;
var dist = vec.Length;
if (dist <= pinpointer.ReachedDistance)
return Distance.REACHED;
else if (dist <= pinpointer.CloseDistance)
return Distance.CLOSE;
else if (dist <= pinpointer.MediumDistance)
return Distance.MEDIUM;
else
return Distance.FAR;
}
}
}