using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Emag.Systems; using Content.Shared.Examine; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; namespace Content.Shared.Pinpointer; public abstract class SharedPinpointerSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly EmagSystem _emag = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnExamined); } /// /// Set the target if capable /// private void OnAfterInteract(EntityUid uid, PinpointerComponent component, AfterInteractEvent args) { if (!args.CanReach || args.Target is not { } target) return; if (!component.CanRetarget || component.IsActive) return; // TODO add doafter once the freeze is lifted args.Handled = true; component.Target = args.Target; _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):player} set target of {ToPrettyString(uid):pinpointer} to {ToPrettyString(component.Target.Value):target}"); if (component.UpdateTargetName) component.TargetName = component.Target == null ? null : Identity.Name(component.Target.Value, EntityManager); } /// /// Set pinpointers target to track /// public virtual 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.UpdateTargetName) pinpointer.TargetName = target == null ? null : Identity.Name(target.Value, EntityManager); if (pinpointer.IsActive) UpdateDirectionToTarget(uid, pinpointer); } /// /// Update direction from pinpointer to selected target (if it was set) /// protected virtual void UpdateDirectionToTarget(EntityUid uid, PinpointerComponent? pinpointer = null) { } private void OnExamined(EntityUid uid, PinpointerComponent component, ExaminedEvent args) { if (!args.IsInDetailsRange || component.TargetName == null) return; args.PushMarkup(Loc.GetString("examine-pinpointer-linked", ("target", component.TargetName))); } /// /// Manually set distance from pinpointer to target /// public void SetDistance(EntityUid uid, Distance distance, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) return; if (distance == pinpointer.DistanceToTarget) return; pinpointer.DistanceToTarget = distance; Dirty(uid, pinpointer); } /// /// Try to manually set pinpointer arrow direction. /// If difference between current angle and new angle is smaller than /// pinpointer precision, new value will be ignored and it will return false. /// public bool TrySetArrowAngle(EntityUid uid, Angle arrowAngle, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) return false; if (pinpointer.ArrowAngle.EqualsApprox(arrowAngle, pinpointer.Precision)) return false; pinpointer.ArrowAngle = arrowAngle; Dirty(uid, pinpointer); return true; } /// /// Activate/deactivate pinpointer screen. If it has target it will start tracking it. /// public void SetActive(EntityUid uid, bool isActive, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) return; if (isActive == pinpointer.IsActive) return; pinpointer.IsActive = isActive; Dirty(uid, pinpointer); } /// /// Toggle Pinpointer screen. If it has target it will start tracking it. /// /// True if pinpointer was activated, false otherwise public virtual bool TogglePinpointer(EntityUid uid, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) return false; var isActive = !pinpointer.IsActive; SetActive(uid, isActive, pinpointer); return isActive; } private void OnEmagged(EntityUid uid, PinpointerComponent component, ref GotEmaggedEvent args) { if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) return; if (_emag.CheckFlag(uid, EmagType.Interaction)) return; if (component.CanRetarget) return; args.Handled = true; component.CanRetarget = true; } }