| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- using Content.Shared.Interaction;
- using Content.Shared.Pinpointer;
- using System.Linq;
- using System.Numerics;
- using Robust.Shared.Utility;
- using Content.Server.Shuttles.Events;
- using Content.Shared.IdentityManagement;
- namespace Content.Server.Pinpointer;
- public sealed class PinpointerSystem : SharedPinpointerSystem
- {
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- private EntityQuery<TransformComponent> _xformQuery;
- public override void Initialize()
- {
- base.Initialize();
- _xformQuery = GetEntityQuery<TransformComponent>();
- SubscribeLocalEvent<PinpointerComponent, ActivateInWorldEvent>(OnActivate);
- SubscribeLocalEvent<FTLCompletedEvent>(OnLocateTarget);
- }
- public override bool TogglePinpointer(EntityUid uid, PinpointerComponent? pinpointer = null)
- {
- if (!Resolve(uid, ref pinpointer))
- return false;
- var isActive = !pinpointer.IsActive;
- SetActive(uid, isActive, pinpointer);
- UpdateAppearance(uid, pinpointer);
- return isActive;
- }
- private void UpdateAppearance(EntityUid uid, PinpointerComponent pinpointer, AppearanceComponent? appearance = null)
- {
- if (!Resolve(uid, ref appearance))
- return;
- _appearance.SetData(uid, PinpointerVisuals.IsActive, pinpointer.IsActive, appearance);
- _appearance.SetData(uid, PinpointerVisuals.TargetDistance, pinpointer.DistanceToTarget, appearance);
- }
- private void OnActivate(EntityUid uid, PinpointerComponent component, ActivateInWorldEvent args)
- {
- if (args.Handled || !args.Complex)
- return;
- TogglePinpointer(uid, component);
- if (!component.CanRetarget)
- LocateTarget(uid, component);
- args.Handled = true;
- }
- private void OnLocateTarget(ref FTLCompletedEvent ev)
- {
- // This feels kind of expensive, but it only happens once per hyperspace jump
- // todo: ideally, you would need to raise this event only on jumped entities
- // this code update ALL pinpointers in game
- var query = EntityQueryEnumerator<PinpointerComponent>();
- while (query.MoveNext(out var uid, out var pinpointer))
- {
- if (pinpointer.CanRetarget)
- continue;
- LocateTarget(uid, pinpointer);
- }
- }
- private void LocateTarget(EntityUid uid, PinpointerComponent component)
- {
- // try to find target from whitelist
- if (component.IsActive && component.Component != null)
- {
- if (!EntityManager.ComponentFactory.TryGetRegistration(component.Component, out var reg))
- {
- Log.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
- var query = EntityQueryEnumerator<PinpointerComponent>();
- while (query.MoveNext(out var uid, out var pinpointer))
- {
- UpdateDirectionToTarget(uid, pinpointer);
- }
- }
- /// <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)
- {
- _xformQuery.Resolve(uid, ref transform, false);
- 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);
- foreach (var (otherUid, _) in EntityManager.GetAllComponents(whitelist))
- {
- if (!_xformQuery.TryGetComponent(otherUid, out var compXform) || compXform.MapID != mapId)
- continue;
- var dist = (_transform.GetWorldPosition(compXform) - worldPos).LengthSquared();
- l.TryAdd(dist, otherUid);
- }
- // return uid with a smallest distance
- return l.Count > 0 ? l.First().Value : null;
- }
- /// <summary>
- /// Update direction from pinpointer to selected target (if it was set)
- /// </summary>
- protected override void UpdateDirectionToTarget(EntityUid uid, PinpointerComponent? pinpointer = null)
- {
- if (!Resolve(uid, ref pinpointer))
- return;
- if (!pinpointer.IsActive)
- return;
- var target = pinpointer.Target;
- if (target == null || !EntityManager.EntityExists(target.Value))
- {
- SetDistance(uid, Distance.Unknown, pinpointer);
- return;
- }
- var dirVec = CalculateDirection(uid, target.Value);
- var oldDist = pinpointer.DistanceToTarget;
- if (dirVec != null)
- {
- var angle = dirVec.Value.ToWorldAngle();
- TrySetArrowAngle(uid, angle, pinpointer);
- var dist = CalculateDistance(dirVec.Value, pinpointer);
- SetDistance(uid, dist, pinpointer);
- }
- else
- {
- SetDistance(uid, Distance.Unknown, pinpointer);
- }
- if (oldDist != pinpointer.DistanceToTarget)
- UpdateAppearance(uid, pinpointer);
- }
- /// <summary>
- /// Calculate direction from pinUid to trgUid
- /// </summary>
- /// <returns>Null if failed to calculate 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(Vector2 vec, PinpointerComponent pinpointer)
- {
- 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;
- }
- }
|