// SPDX-FileCopyrightText: 2025 Ark // SPDX-FileCopyrightText: 2025 Ilya246 // SPDX-FileCopyrightText: 2025 Redrover1760 // SPDX-FileCopyrightText: 2025 ark1368 // // SPDX-License-Identifier: AGPL-3.0-or-later using System.Numerics; using Content.Shared._Goobstation.Vehicles; using Content.Shared._Mono.Radar; using Content.Shared.Movement.Components; using Content.Shared.Projectiles; using Content.Shared.Shuttles.Components; using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; namespace Content.Server._Mono.Radar; public sealed partial class RadarBlipSystem : EntitySystem { [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; public override void Initialize() { base.Initialize(); SubscribeNetworkEvent(OnBlipsRequested); SubscribeLocalEvent(OnBlipShutdown); } 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 hitscans = AssembleHitscanReport((EntityUid)radarUid, radar); // Combine the blips and hitscan lines var giveEv = new GiveBlipsEvent(blips, hitscans); RaiseNetworkEvent(giveEv, args.SenderSession); blips.Clear(); hitscans.Clear(); } private void OnBlipShutdown(EntityUid blipUid, RadarBlipComponent component, ComponentShutdown args) { var netBlipUid = GetNetEntity(blipUid); var removalEv = new BlipRemovalEvent(netBlipUid); RaiseNetworkEvent(removalEv); } private List<(NetEntity netUid, NetCoordinates Position, Vector2 Vel, float Scale, Color Color, RadarBlipShape Shape)> AssembleBlipsReport(EntityUid uid, RadarConsoleComponent? component = null) { var blips = new List<(NetEntity netUid, NetCoordinates Position, Vector2 Vel, float Scale, Color Color, RadarBlipShape Shape)>(); if (Resolve(uid, ref component)) { var radarXform = Transform(uid); var radarPosition = _xform.GetWorldPosition(uid); var radarGrid = _xform.GetGrid(uid); var radarMapId = radarXform.MapID; // Check if the radar is on an FTL map var isFtlMap = HasComp(radarXform.GridUid); var blipQuery = EntityQueryEnumerator(); while (blipQuery.MoveNext(out var blipUid, out var blip, out var blipXform, out var blipPhysics)) { if (!blip.Enabled) continue; // This prevents blips from showing on radars that are on different maps if (blipXform.MapID != radarMapId) continue; var netBlipUid = GetNetEntity(blipUid); var blipGrid = blipXform.GridUid; // if (HasComp(blipUid)) // { // // Skip if in FTL // if (isFtlMap) // continue; // // // Skip if no grid // if (blipGrid == null) // continue; // // // Ensure the grid is a valid MapGrid // if (!HasComp(blipGrid.Value)) // continue; // // // Ensure the shield is a direct child of the grid // if (blipXform.ParentUid != blipGrid) // continue; // } var blipVelocity = _physics.GetMapLinearVelocity(blipUid, blipPhysics, blipXform); var distance = (_xform.GetWorldPosition(blipXform) - radarPosition).Length(); if (distance > component.MaxRange) continue; if (blip.RequireNoGrid && blipGrid != null // if we want no grid but we are on a grid || !blip.VisibleFromOtherGrids && blipGrid != radarGrid) // or if we don't want to be visible from other grids but we're on another grid continue; // don't show this blip // due to PVS being a thing, things will break if we try to parent to not the map or a grid var coord = blipXform.Coordinates; if (blipXform.ParentUid != blipXform.MapUid && blipXform.ParentUid != blipGrid) coord = _xform.WithEntityId(coord, blipGrid ?? blipXform.MapUid!.Value); // we're parented to either the map or a grid and this is relative velocity so account for grid movement if (blipGrid != null) blipVelocity -= _physics.GetLinearVelocity(blipGrid.Value, coord.Position); blips.Add((netBlipUid, GetNetCoordinates(coord), blipVelocity, blip.Scale, blip.RadarColor, blip.Shape)); } } return blips; } /// /// Assembles trajectory information for hitscan projectiles to be displayed on radar /// private List<(Vector2 Start, Vector2 End, float Thickness, Color Color)> AssembleHitscanReport(EntityUid uid, RadarConsoleComponent? component = null) { var hitscans = new List<(Vector2 Start, Vector2 End, float Thickness, Color Color)>(); if (!Resolve(uid, ref component)) return hitscans; var radarXform = Transform(uid); var radarPosition = _xform.GetWorldPosition(uid); var radarGrid = _xform.GetGrid(uid); var radarMapId = radarXform.MapID; var hitscanQuery = EntityQueryEnumerator(); while (hitscanQuery.MoveNext(out var hitscanUid, out var hitscan)) { if (!hitscan.Enabled) continue; // Check if either the start or end point is within radar range var startDistance = (hitscan.StartPosition - radarPosition).Length(); var endDistance = (hitscan.EndPosition - radarPosition).Length(); if (startDistance > component.MaxRange && endDistance > component.MaxRange) continue; hitscans.Add((hitscan.StartPosition, hitscan.EndPosition, hitscan.LineThickness, hitscan.RadarColor)); } return hitscans; } /// /// Configures the radar blip for a jetpack or vehicle entity. /// private void SetupRadarBlip(EntityUid uid, Color color, float scale, bool visibleFromOtherGrids = true, bool requireNoGrid = false) { var blip = EnsureComp(uid); blip.RadarColor = color; blip.Scale = scale; blip.VisibleFromOtherGrids = visibleFromOtherGrids; blip.RequireNoGrid = requireNoGrid; } /// /// Adds radar blip to jetpacks when they are activated. /// private void OnJetpackActivated(EntityUid uid, ActiveJetpackComponent component, ComponentAdd args) { SetupRadarBlip(uid, Color.Cyan, 1f, true, true); } /// /// Removes radar blip from jetpacks when they are deactivated. /// private void OnJetpackDeactivated(EntityUid uid, ActiveJetpackComponent component, ComponentRemove args) { RemComp(uid); } /// /// Configures the radar blip for a vehicle entity. /// public void SetupVehicleRadarBlip(Entity uid) { SetupRadarBlip(uid, Color.Cyan, 1f, true, true); } }