diff --git a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs index 805608c9a5..cc85dd4832 100644 --- a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Client._Hullrot.Radar; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; @@ -13,6 +14,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Client.Shuttles.UI; @@ -23,6 +25,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl [Dependency] private readonly IMapManager _mapManager = default!; private readonly SharedShuttleSystem _shuttles; private readonly SharedTransformSystem _transform; + private readonly RadarBlipsSystem _blips; /// /// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid. @@ -49,11 +52,31 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl private List> _grids = new(); + #region Hullrot + // These 2 handle timing updates + private const float RadarUpdateInterval = 2f; + private float _updateAccumulator = 0f; + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + _updateAccumulator += args.DeltaSeconds; + + if (_updateAccumulator >= RadarUpdateInterval) + { + _updateAccumulator = 0; // I'm not subtracting because frame updates can majorly lag in a way normal ones cannot. + + if (_consoleEntity != null) + _blips.RequestBlips((EntityUid)_consoleEntity); + } + } + #endregion Hullrot + public ShuttleNavControl() : base(64f, 256f, 256f) { RobustXamlLoader.Load(this); _shuttles = EntManager.System(); _transform = EntManager.System(); + _blips = EntManager.System(); } public void SetMatrix(EntityCoordinates? coordinates, Angle? angle) @@ -290,6 +313,25 @@ protected override void Draw(DrawingHandleScreen handle) } } + #region Hullrot + // Draw radar line + // First, figure out which angle to draw. + var updateRatio = _updateAccumulator / RadarUpdateInterval; + + Angle angle = updateRatio * Math.Tau; + var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y)); + handle.DrawLine(origin, origin + angle.ToVec() * ScaledMinimapRadius * 1.42f, Color.Orange.WithAlpha(0.1f)); + + // Draw blips + var blips = _blips.GetCurrentBlips(); + + foreach (var blip in blips) + { + var blipPos = Vector2.Transform(blip.Item1, worldToShuttle * shuttleToView); + handle.DrawCircle(blipPos, blip.Item2 * 3f, blip.Item3.WithAlpha(0.8f)); + } + + #endregion } private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 gridToView) diff --git a/Content.Client/_Hullrot/Radar/RadarBlipsSystem.cs b/Content.Client/_Hullrot/Radar/RadarBlipsSystem.cs new file mode 100644 index 0000000000..16adcecbd4 --- /dev/null +++ b/Content.Client/_Hullrot/Radar/RadarBlipsSystem.cs @@ -0,0 +1,48 @@ +using System.Numerics; +using Content.Shared._Hullrot.Radar; +using Robust.Shared.Timing; + +namespace Content.Client._Hullrot.Radar; + +public sealed partial class RadarBlipsSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + private TimeSpan _lastUpdatedTime; + private List<(Vector2, float, Color)> _blips = new(); + + public override void Initialize() + { + base.Initialize(); + SubscribeNetworkEvent(HandleReceiveBlips); + } + + private void HandleReceiveBlips(GiveBlipsEvent ev, EntitySessionEventArgs args) + { + Logger.Error("Received blips. Count: " + ev.Blips.Count); + foreach (var blip in ev.Blips) + { + Logger.Error("Pos: " + blip.Item1); + Logger.Error("Scale: " + blip.Item2); + Logger.Error("Color: " + blip.Item3); + } + + _blips = ev.Blips; + _lastUpdatedTime = _timing.CurTime; + } + + public void RequestBlips(EntityUid console) + { + var netConsole = GetNetEntity(console); + + var ev = new RequestBlipsEvent(netConsole); + RaiseNetworkEvent(ev); + } + + public List<(Vector2, float, Color)> GetCurrentBlips() + { + if (_timing.CurTime.TotalSeconds - _lastUpdatedTime.TotalSeconds > 10) + return new List<(Vector2, float, Color)>(); + + return _blips; + } +} diff --git a/Content.Server/_Hullrot/Radar/RadarBlipComponent.cs b/Content.Server/_Hullrot/Radar/RadarBlipComponent.cs new file mode 100644 index 0000000000..4bd563dae2 --- /dev/null +++ b/Content.Server/_Hullrot/Radar/RadarBlipComponent.cs @@ -0,0 +1,29 @@ +namespace Content.Server._Hullrot.Radar; + +/// +/// These handle objects which should be represented by radar blips. +/// +[RegisterComponent] +public sealed partial class RadarBlipComponent : Component +{ + /// + /// Color of the blip. + /// + [DataField] + public Color Color = Color.Red; + + /// + /// Scale of the blip. + /// + [DataField] + public float Scale = 1; + + /// + /// Whether this blip should be shown even when parented to a grid. + /// + [DataField] + public bool RequireNoGrid = false; + + [DataField] + public bool Enabled = true; +} diff --git a/Content.Server/_Hullrot/Radar/RadarBlipSystem.cs b/Content.Server/_Hullrot/Radar/RadarBlipSystem.cs new file mode 100644 index 0000000000..e685874593 --- /dev/null +++ b/Content.Server/_Hullrot/Radar/RadarBlipSystem.cs @@ -0,0 +1,67 @@ +using System.Numerics; +using Content.Server.Shuttles.Systems; +using Content.Shared._Hullrot.Radar; +using Content.Shared.Shuttles.Components; + +namespace Content.Server._Hullrot.Radar; + +/// +/// I debated making partial +/// but ended up doing this instead to mnimize conflicts. This system +/// handles radar blips -- which, due to both the limitations of PVS range +/// and against giving the client too much info must be server side. +/// +public sealed partial class RadarBlipSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _xform = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeNetworkEvent(OnBlipsRequested); + } + + private void OnBlipsRequested(RequestBlipsEvent ev, EntitySessionEventArgs args) + { + if (!TryGetEntity(ev.Radar, out var radarUid)) + return; + + if (!TryComp(radarUid, out var radar)) + return; + + var blips = AssembleBlipsReport((EntityUid)radarUid, radar); + + var giveEv = new GiveBlipsEvent(blips); + RaiseNetworkEvent(giveEv, args.SenderSession); + } + + private List<(Vector2, float, Color)> AssembleBlipsReport(EntityUid uid, RadarConsoleComponent? component = null) + { + var blips = new List<(Vector2, float, Color)>(); + + if (Resolve(uid, ref component)) + { + var blipQuery = EntityQueryEnumerator(); + + // add blips, except + while (blipQuery.MoveNext(out var blipUid, out var blip, out var _)) + { + // case 1: component disabled + if (!blip.Enabled) + continue; + + // case 2: blip out of radar's max range + var distance = (_xform.GetWorldPosition(blipUid) - _xform.GetWorldPosition(uid)).Length(); + if (distance > component.MaxRange) + continue; + + // case 3: On grid but will only show up off grid + if (blip.RequireNoGrid && _xform.GetGrid(blipUid) != null) + continue; + + blips.Add((_xform.GetWorldPosition(blipUid), blip.Scale, blip.Color)); + } + } + + return blips; + } +} diff --git a/Content.Shared/_Hullrot/Radar/RadarMessages.cs b/Content.Shared/_Hullrot/Radar/RadarMessages.cs new file mode 100644 index 0000000000..fc6fd3bd16 --- /dev/null +++ b/Content.Shared/_Hullrot/Radar/RadarMessages.cs @@ -0,0 +1,31 @@ +using System.Numerics; +using Robust.Shared.Serialization; + +namespace Content.Shared._Hullrot.Radar; + +/// These are messages for exchanging information about radar signatures +/// between the client and server. See the Server's RadarBlipSystem and +/// the Client's ShuttleNavControl. + +[Serializable, NetSerializable] +public sealed class GiveBlipsEvent : EntityEventArgs +{ + /// + /// Blips are a position, a scale, and a color. + /// + public readonly List<(Vector2, float, Color)> Blips; + public GiveBlipsEvent(List<(Vector2, float, Color)> blips) + { + Blips = blips; + } +} + +[Serializable, NetSerializable] +public sealed class RequestBlipsEvent : EntityEventArgs +{ + public NetEntity Radar; + public RequestBlipsEvent(NetEntity radar) + { + Radar = radar; + } +}