diff --git a/Content.Client/Pinpointer/ClientPinpointerSystem.cs b/Content.Client/Pinpointer/ClientPinpointerSystem.cs index d31aad8432..afa5b23776 100644 --- a/Content.Client/Pinpointer/ClientPinpointerSystem.cs +++ b/Content.Client/Pinpointer/ClientPinpointerSystem.cs @@ -1,10 +1,7 @@ using Content.Shared.Pinpointer; using Robust.Client.GameObjects; using Robust.Client.Graphics; -using Robust.Shared.GameObjects; using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Maths; namespace Content.Client.Pinpointer { @@ -40,9 +37,9 @@ namespace Content.Client.Pinpointer if (args.Current is not PinpointerComponentState state) return; - SetActive(uid, state.IsActive, pinpointer); - SetDirection(uid, state.DirectionToTarget, pinpointer); - SetDistance(uid, state.DistanceToTarget, pinpointer); + pinpointer.IsActive = state.IsActive; + pinpointer.ArrowAngle = state.ArrowAngle; + pinpointer.DistanceToTarget = state.DistanceToTarget; } private void UpdateAppearance(EntityUid uid, PinpointerComponent? pinpointer = null, @@ -55,13 +52,13 @@ namespace Content.Client.Pinpointer _appearance.SetData(uid, PinpointerVisuals.TargetDistance, pinpointer.DistanceToTarget, appearance); } - private void UpdateDirAppearance(EntityUid uid, Direction dir,PinpointerComponent? pinpointer = null, + private void UpdateArrowAngle(EntityUid uid, Angle angle, PinpointerComponent? pinpointer = null, AppearanceComponent? appearance = null) { if (!Resolve(uid, ref pinpointer, ref appearance)) return; - _appearance.SetData(uid, PinpointerVisuals.TargetDirection, dir, appearance); + _appearance.SetData(uid, PinpointerVisuals.ArrowAngle, angle, appearance); } /// @@ -70,20 +67,12 @@ namespace Content.Client.Pinpointer /// private void UpdateEyeDir(EntityUid uid, PinpointerComponent? pinpointer = null) { - if (!Resolve(uid, ref pinpointer)) + if (!Resolve(uid, ref pinpointer) || !pinpointer.HasTarget) return; - var worldDir = pinpointer.DirectionToTarget; - if (worldDir == Direction.Invalid) - { - UpdateDirAppearance(uid, Direction.Invalid, pinpointer); - return; - } - var eye = _eyeManager.CurrentEye; - var angle = worldDir.ToAngle() + eye.Rotation; - var eyeDir = angle.GetDir(); - UpdateDirAppearance(uid, eyeDir, pinpointer); + var angle = pinpointer.ArrowAngle + eye.Rotation; + UpdateArrowAngle(uid, angle, pinpointer); } } } diff --git a/Content.Client/Pinpointer/PinpointerVisualizer.cs b/Content.Client/Pinpointer/PinpointerVisualizer.cs deleted file mode 100644 index 9236fede5b..0000000000 --- a/Content.Client/Pinpointer/PinpointerVisualizer.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Content.Shared.Pinpointer; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Maths; - -namespace Content.Client.Pinpointer -{ - [UsedImplicitly] - public sealed class PinpointerVisualizer : AppearanceVisualizer - { - [Obsolete("Subscribe to AppearanceChangeEvent instead.")] - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(component.Owner, out SpriteComponent? sprite)) - return; - - // check if pinpointer screen is active - if (!component.TryGetData(PinpointerVisuals.IsActive, out bool isActive) || !isActive) - { - sprite.LayerSetVisible(PinpointerLayers.Screen, false); - return; - } - - // check if it has direction to target - sprite.LayerSetVisible(PinpointerLayers.Screen, true); - sprite.LayerSetRotation(PinpointerLayers.Screen, Angle.Zero); - - if (!component.TryGetData(PinpointerVisuals.TargetDirection, out Direction dir) || dir == Direction.Invalid) - { - sprite.LayerSetState(PinpointerLayers.Screen, "pinonnull"); - return; - } - - // check distance to target - if (!component.TryGetData(PinpointerVisuals.TargetDistance, out Distance dis)) - dis = Distance.UNKNOWN; - - switch (dis) - { - case Distance.REACHED: - sprite.LayerSetState(PinpointerLayers.Screen, "pinondirect"); - break; - case Distance.CLOSE: - sprite.LayerSetState(PinpointerLayers.Screen, "pinonclose"); - sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle()); - break; - case Distance.MEDIUM: - sprite.LayerSetState(PinpointerLayers.Screen, "pinonmedium"); - sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle()); - break; - case Distance.FAR: - case Distance.UNKNOWN: - sprite.LayerSetState(PinpointerLayers.Screen, "pinonfar"); - sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle()); - break; - } - } - } -} diff --git a/Content.Client/Pinpointer/PinpointerVisualizerSystem.cs b/Content.Client/Pinpointer/PinpointerVisualizerSystem.cs new file mode 100644 index 0000000000..244dc00f4c --- /dev/null +++ b/Content.Client/Pinpointer/PinpointerVisualizerSystem.cs @@ -0,0 +1,60 @@ +using Content.Shared.Pinpointer; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Pinpointer +{ + [UsedImplicitly] + public sealed class PinpointerVisualizerSystem : VisualizerSystem + { + protected override void OnAppearanceChange(EntityUid uid, PinpointerComponent component, ref AppearanceChangeEvent args) + { + base.OnAppearanceChange(uid, component, ref args); + + if (!TryComp(component.Owner, out SpriteComponent? sprite)) + return; + + // check if pinpointer screen is active + if (!args.Component.TryGetData(PinpointerVisuals.IsActive, out bool isActive) || !isActive) + { + sprite.LayerSetVisible(PinpointerLayers.Screen, false); + return; + } + + sprite.LayerSetVisible(PinpointerLayers.Screen, true); + + // check distance and direction to target + if (!args.Component.TryGetData(PinpointerVisuals.TargetDistance, out Distance dis) || + !args.Component.TryGetData(PinpointerVisuals.ArrowAngle, out Angle angle)) + { + sprite.LayerSetState(PinpointerLayers.Screen, "pinonnull"); + sprite.LayerSetRotation(PinpointerLayers.Screen, Angle.Zero); + return; + } + + switch (dis) + { + case Distance.Reached: + sprite.LayerSetState(PinpointerLayers.Screen, "pinondirect"); + sprite.LayerSetRotation(PinpointerLayers.Screen, Angle.Zero); + break; + case Distance.Close: + sprite.LayerSetState(PinpointerLayers.Screen, "pinonclose"); + sprite.LayerSetRotation(PinpointerLayers.Screen, angle); + break; + case Distance.Medium: + sprite.LayerSetState(PinpointerLayers.Screen, "pinonmedium"); + sprite.LayerSetRotation(PinpointerLayers.Screen, angle); + break; + case Distance.Far: + sprite.LayerSetState(PinpointerLayers.Screen, "pinonfar"); + sprite.LayerSetRotation(PinpointerLayers.Screen, angle); + break; + case Distance.Unknown: + sprite.LayerSetState(PinpointerLayers.Screen, "pinonnull"); + sprite.LayerSetRotation(PinpointerLayers.Screen, Angle.Zero); + break; + } + } + } +} diff --git a/Content.Server/Pinpointer/ServerPinpointerSystem.cs b/Content.Server/Pinpointer/ServerPinpointerSystem.cs index a785ab83d9..a1c7b5af74 100644 --- a/Content.Server/Pinpointer/ServerPinpointerSystem.cs +++ b/Content.Server/Pinpointer/ServerPinpointerSystem.cs @@ -26,12 +26,12 @@ namespace Content.Server.Pinpointer private void OnLocateTarget(HyperspaceJumpCompletedEvent ev) { // This feels kind of expensive, but it only happens once per hyperspace jump - foreach (var uid in ActivePinpointers) + + // todo: ideally, you would need to raise this event only on jumped entities + // this code update ALL pinpointers in game + foreach (var pinpointer in EntityQuery()) { - if (TryComp(uid, out var component)) - { - LocateTarget(uid, component); - } + LocateTarget(pinpointer.Owner, pinpointer); } } @@ -58,9 +58,9 @@ namespace Content.Server.Pinpointer // because target or pinpointer can move // we need to update pinpointers arrow each frame - foreach (var uid in ActivePinpointers) + foreach (var pinpointer in EntityQuery()) { - UpdateDirectionToTarget(uid); + UpdateDirectionToTarget(pinpointer.Owner, pinpointer); } } @@ -85,14 +85,14 @@ namespace Content.Server.Pinpointer foreach (var comp in EntityManager.GetAllComponents(whitelist)) { - if (!xformQuery.TryGetComponent(comp.Owner, out var compXform) || - compXform.MapID != mapId) continue; + 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 uid with a smallest distance return l.Count > 0 ? l.First().Value : null; } @@ -120,33 +120,34 @@ namespace Content.Server.Pinpointer if (!Resolve(uid, ref pinpointer)) return; + if (!pinpointer.IsActive) + return; + var target = pinpointer.Target; if (target == null || !EntityManager.EntityExists(target.Value)) { - SetDirection(uid, Direction.Invalid, pinpointer); - SetDistance(uid, Distance.UNKNOWN, 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 angle = dirVec.Value.ToWorldAngle(); + TrySetArrowAngle(uid, angle, pinpointer); var dist = CalculateDistance(uid, dirVec.Value, pinpointer); SetDistance(uid, dist, pinpointer); } else { - SetDirection(uid, Direction.Invalid, pinpointer); - SetDistance(uid, Distance.UNKNOWN, pinpointer); + SetDistance(uid, Distance.Unknown, pinpointer); } } /// /// Calculate direction from pinUid to trgUid /// - /// Null if failed to caluclate distance between two entities + /// Null if failed to calculate distance between two entities private Vector2? CalculateDirection(EntityUid pinUid, EntityUid trgUid) { var xformQuery = GetEntityQuery(); @@ -169,17 +170,17 @@ namespace Content.Server.Pinpointer private Distance CalculateDistance(EntityUid uid, Vector2 vec, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) - return Distance.UNKNOWN; + return Distance.Unknown; var dist = vec.Length; if (dist <= pinpointer.ReachedDistance) - return Distance.REACHED; + return Distance.Reached; else if (dist <= pinpointer.CloseDistance) - return Distance.CLOSE; + return Distance.Close; else if (dist <= pinpointer.MediumDistance) - return Distance.MEDIUM; + return Distance.Medium; else - return Distance.FAR; + return Distance.Far; } } } diff --git a/Content.Shared/Pinpointer/PinpointerComponent.cs b/Content.Shared/Pinpointer/PinpointerComponent.cs index 238b35a053..7e467f14b0 100644 --- a/Content.Shared/Pinpointer/PinpointerComponent.cs +++ b/Content.Shared/Pinpointer/PinpointerComponent.cs @@ -24,27 +24,34 @@ namespace Content.Shared.Pinpointer [DataField("reachedDistance")] public float ReachedDistance = 1f; + /// + /// Pinpointer arrow precision in radians. + /// + [DataField("precision")] + public double Precision = 0.09; + public EntityUid? Target = null; public bool IsActive = false; - public Direction DirectionToTarget = Direction.Invalid; - public Distance DistanceToTarget = Distance.UNKNOWN; + public Angle ArrowAngle; + public Distance DistanceToTarget = Distance.Unknown; + public bool HasTarget => DistanceToTarget != Distance.Unknown; } [Serializable, NetSerializable] public sealed class PinpointerComponentState : ComponentState { public bool IsActive; - public Direction DirectionToTarget; + public Angle ArrowAngle; public Distance DistanceToTarget; } [Serializable, NetSerializable] public enum Distance : byte { - UNKNOWN, - REACHED, - CLOSE, - MEDIUM, - FAR + Unknown, + Reached, + Close, + Medium, + Far } } diff --git a/Content.Shared/Pinpointer/PinpointerVisuals.cs b/Content.Shared/Pinpointer/PinpointerVisuals.cs index fd190f068a..a7e9c8bf0f 100644 --- a/Content.Shared/Pinpointer/PinpointerVisuals.cs +++ b/Content.Shared/Pinpointer/PinpointerVisuals.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Pinpointer public enum PinpointerVisuals : byte { IsActive, - TargetDirection, + ArrowAngle, TargetDistance } diff --git a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs index 775f001019..17e5de8cfd 100644 --- a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs +++ b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs @@ -4,13 +4,10 @@ namespace Content.Shared.Pinpointer { public abstract class SharedPinpointerSystem : EntitySystem { - protected readonly HashSet ActivePinpointers = new(); - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(GetCompState); - SubscribeLocalEvent(OnPinpointerShutdown); } private void GetCompState(EntityUid uid, PinpointerComponent pinpointer, ref ComponentGetState args) @@ -18,17 +15,11 @@ namespace Content.Shared.Pinpointer args.State = new PinpointerComponentState { IsActive = pinpointer.IsActive, - DirectionToTarget = pinpointer.DirectionToTarget, + ArrowAngle = pinpointer.ArrowAngle, DistanceToTarget = pinpointer.DistanceToTarget }; } - private void OnPinpointerShutdown(EntityUid uid, PinpointerComponent component, ComponentShutdown _) - { - // no need to dirty it/etc: it's shutting down anyway! - ActivePinpointers.Remove(uid); - } - /// /// Manually set distance from pinpointer to target /// @@ -45,18 +36,22 @@ namespace Content.Shared.Pinpointer } /// - /// Manually set pinpointer arrow direction + /// 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 void SetDirection(EntityUid uid, Direction directionToTarget, PinpointerComponent? pinpointer = null) + public bool TrySetArrowAngle(EntityUid uid, Angle arrowAngle, PinpointerComponent? pinpointer = null) { if (!Resolve(uid, ref pinpointer)) - return; + return false; - if (directionToTarget == pinpointer.DirectionToTarget) - return; + if (pinpointer.ArrowAngle.EqualsApprox(arrowAngle, pinpointer.Precision)) + return false; - pinpointer.DirectionToTarget = directionToTarget; + pinpointer.ArrowAngle = arrowAngle; Dirty(pinpointer); + + return true; } /// @@ -68,13 +63,7 @@ namespace Content.Shared.Pinpointer return; if (isActive == pinpointer.IsActive) return; - - // add-remove pinpointer from update list - if (isActive) - ActivePinpointers.Add(uid); - else - ActivePinpointers.Remove(uid); - + pinpointer.IsActive = isActive; Dirty(pinpointer); } diff --git a/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml b/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml index 905dbe9d9e..48682b4547 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml @@ -9,6 +9,7 @@ noRot: True - type: Sprite netsync: false + noRot: True sprite: Objects/Devices/pinpointer.rsi layers: - state: pinpointer @@ -19,8 +20,6 @@ sprite: Objects/Devices/pinpointer.rsi - type: Pinpointer - type: Appearance - visuals: - - type: PinpointerVisualizer - type: entity name: pinpointer diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 041587116f..1905f9f55e 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -560,6 +560,8 @@ public sealed class $CLASS$ : Shared$CLASS$ { True True True + True + True True True True