using Content.Shared.Medical.CrewMonitoring;
-using Robust.Client.GameObjects;
-namespace Content.Client.Medical.CrewMonitoring
+namespace Content.Client.Medical.CrewMonitoring;
+
+public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
{
- public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
+ [ViewVariables]
+ private CrewMonitoringWindow? _menu;
+
+ public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
- [ViewVariables]
- private CrewMonitoringWindow? _menu;
+ }
- public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
- {
- }
+ protected override void Open()
+ {
+ EntityUid? gridUid = null;
+ string stationName = string.Empty;
- protected override void Open()
+ if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
{
- EntityUid? gridUid = null;
+ gridUid = xform.GridUid;
- if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
+ if (EntMan.TryGetComponent<MetaDataComponent>(gridUid, out var metaData))
{
- gridUid = xform.GridUid;
+ stationName = metaData.EntityName;
}
-
- _menu = new CrewMonitoringWindow(gridUid);
-
- _menu.OpenCentered();
- _menu.OnClose += Close;
}
- protected override void UpdateState(BoundUserInterfaceState state)
- {
- base.UpdateState(state);
+ _menu = new CrewMonitoringWindow(stationName, gridUid);
- switch (state)
- {
- case CrewMonitoringState st:
- EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
+ _menu.OpenCentered();
+ _menu.OnClose += Close;
+ }
- _menu?.ShowSensors(st.Sensors, xform?.Coordinates, st.Snap, st.Precision);
- break;
- }
- }
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
- protected override void Dispose(bool disposing)
+ switch (state)
{
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Dispose();
+ case CrewMonitoringState st:
+ EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
+ _menu?.ShowSensors(st.Sensors, Owner, xform?.Coordinates);
+ break;
}
}
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ _menu?.Dispose();
+ }
}
--- /dev/null
+using Content.Client.Pinpointer.UI;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.Medical.CrewMonitoring;
+
+public sealed partial class CrewMonitoringNavMapControl : NavMapControl
+{
+ public NetEntity? Focus;
+ public Dictionary<NetEntity, string> LocalizedNames = new();
+
+ private Color _backgroundColor;
+ private Label _trackedEntityLabel;
+ private PanelContainer _trackedEntityPanel;
+
+ public CrewMonitoringNavMapControl() : base()
+ {
+ WallColor = new Color(250, 146, 255);
+ TileColor = new(71, 42, 72);
+
+ _backgroundColor = Color.FromSrgb(TileColor.WithAlpha(0.8f));
+
+ _trackedEntityLabel = new Label
+ {
+ Margin = new Thickness(10f, 8f),
+ HorizontalAlignment = HAlignment.Center,
+ VerticalAlignment = VAlignment.Center,
+ Modulate = Color.White,
+ };
+
+ _trackedEntityPanel = new PanelContainer
+ {
+ PanelOverride = new StyleBoxFlat
+ {
+ BackgroundColor = _backgroundColor,
+ },
+
+ Margin = new Thickness(5f, 10f),
+ HorizontalAlignment = HAlignment.Left,
+ VerticalAlignment = VAlignment.Bottom,
+ Visible = false,
+ };
+
+ _trackedEntityPanel.AddChild(_trackedEntityLabel);
+ this.AddChild(_trackedEntityPanel);
+ }
+
+ protected override void Draw(DrawingHandleScreen handle)
+ {
+ base.Draw(handle);
+
+ if (Focus == null)
+ {
+ _trackedEntityLabel.Text = string.Empty;
+ _trackedEntityPanel.Visible = false;
+
+ return;
+ }
+
+ foreach ((var netEntity, var blip) in TrackedEntities)
+ {
+ if (netEntity != Focus)
+ continue;
+
+ if (!LocalizedNames.TryGetValue(netEntity, out var name))
+ name = "Unknown";
+
+ var message = name + "\nLocation: [x = " + MathF.Round(blip.Coordinates.X) + ", y = " + MathF.Round(blip.Coordinates.Y) + "]";
+
+ _trackedEntityLabel.Text = message;
+ _trackedEntityPanel.Visible = true;
+
+ return;
+ }
+
+ _trackedEntityLabel.Text = string.Empty;
+ _trackedEntityPanel.Visible = false;
+ }
+}
<controls:FancyWindow xmlns="https://spacestation14.io"
- xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
+ xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'crew-monitoring-user-interface-title'}"
- SetSize="1130 700"
- MinSize="1130 700">
- <BoxContainer Orientation="Horizontal">
- <ScrollContainer HorizontalExpand="True"
- VerticalExpand="True"
- Margin="8, 8, 8, 8">
- <GridContainer Name="SensorsTable"
- HorizontalExpand="True"
- VerticalExpand="True"
- HSeparationOverride="5"
- VSeparationOverride="20"
- Columns="4">
- <!-- Table header -->
- <Label Text="{Loc 'crew-monitoring-user-interface-name'}"
- StyleClasses="LabelHeading"/>
- <Label Text="{Loc 'crew-monitoring-user-interface-job'}"
- StyleClasses="LabelHeading"/>
- <Label Text="{Loc 'crew-monitoring-user-interface-status'}"
- StyleClasses="LabelHeading"/>
- <Label Text="{Loc 'crew-monitoring-user-interface-location'}"
- StyleClasses="LabelHeading"/>
+ SetSize="1200 700"
+ MinSize="1200 700">
+ <BoxContainer Orientation="Vertical">
+ <BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
+ <ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
+ <BoxContainer Orientation="Vertical">
+ <controls:StripeBack>
+ <PanelContainer>
+ <Label Name="StationName" Text="Unknown station" Align="Center" />
+ </PanelContainer>
+ </controls:StripeBack>
+
+ <ScrollContainer Name="SensorScroller"
+ VerticalExpand="True"
+ SetWidth="520"
+ Margin="8, 8, 8, 8">
+ <BoxContainer Name="SensorsTable"
+ Orientation="Vertical"
+ HorizontalExpand="True"
+ Margin="0 0 10 0">
+ <!-- Table rows are filled by code -->
+ </BoxContainer>
+ <Label Name="NoServerLabel"
+ Text="{Loc 'crew-monitoring-user-interface-no-server'}"
+ StyleClasses="LabelHeading"
+ FontColorOverride="Red"
+ HorizontalAlignment="Center"
+ Visible="false"/>
+ </ScrollContainer>
+ </BoxContainer>
+ </BoxContainer>
- <!-- Table rows are filled by code -->
- </GridContainer>
- <Label Name="NoServerLabel"
- Text="{Loc 'crew-monitoring-user-interface-no-server'}"
- StyleClasses="LabelHeading"
- FontColorOverride="Red"
- HorizontalAlignment="Center"
- Visible="false"/>
- </ScrollContainer>
- <ui:NavMapControl Name="NavMap"
- Margin="5 5"/>
+ <!-- Footer -->
+ <BoxContainer Orientation="Vertical">
+ <PanelContainer StyleClasses="LowDivider" />
+ <BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
+ <Label Text="{Loc 'crew-monitoring-user-interface-flavor-left'}" StyleClasses="WindowFooterText" />
+ <Label Text="{Loc 'crew-monitoring-user-interface-flavor-right'}" StyleClasses="WindowFooterText"
+ HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
+ <TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
+ VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
+ </BoxContainer>
+ </BoxContainer>
</BoxContainer>
</controls:FancyWindow>
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
+using Content.Client.Pinpointer.UI;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Medical.SuitSensor;
+using Content.Shared.StatusIcon;
using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer;
-namespace Content.Client.Medical.CrewMonitoring
+namespace Content.Client.Medical.CrewMonitoring;
+
+[GenerateTypedNameReferences]
+public sealed partial class CrewMonitoringWindow : FancyWindow
{
- [GenerateTypedNameReferences]
- public sealed partial class CrewMonitoringWindow : FancyWindow
+ private List<Control> _rowsContent = new();
+ private readonly IEntityManager _entManager;
+ private readonly IPrototypeManager _prototypeManager;
+ private readonly SpriteSystem _spriteSystem;
+
+ private NetEntity? _trackedEntity;
+ private bool _tryToScrollToListFocus;
+ private Texture? _blipTexture;
+
+ public CrewMonitoringWindow(string stationName, EntityUid? mapUid)
+ {
+ RobustXamlLoader.Load(this);
+
+ _entManager = IoCManager.Resolve<IEntityManager>();
+ _prototypeManager = IoCManager.Resolve<IPrototypeManager>();
+ _spriteSystem = _entManager.System<SpriteSystem>();
+
+ _blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
+
+ if (_entManager.TryGetComponent<TransformComponent>(mapUid, out var xform))
+ NavMap.MapUid = xform.GridUid;
+
+ else
+ NavMap.Visible = false;
+
+ StationName.AddStyleClass("LabelBig");
+ StationName.Text = stationName;
+
+ NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
+ NavMap.ForceNavMapUpdate();
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
{
- private List<Control> _rowsContent = new();
- private List<(DirectionIcon Icon, Vector2 Position)> _directionIcons = new();
- private readonly IEntityManager _entManager;
- private readonly IEyeManager _eye;
- private EntityUid? _stationUid;
- private CrewMonitoringButton? _trackedButton;
+ base.FrameUpdate(args);
- public static int IconSize = 16; // XAML has a `VSeparationOverride` of 20 for each row.
+ if (_tryToScrollToListFocus)
+ TryToScrollToFocus();
+ }
- public CrewMonitoringWindow(EntityUid? mapUid)
+ public void ShowSensors(List<SuitSensorStatus> sensors, EntityUid monitor, EntityCoordinates? monitorCoords)
+ {
+ ClearOutDatedData();
+
+ // No server label
+ if (sensors.Count == 0)
{
- RobustXamlLoader.Load(this);
- _eye = IoCManager.Resolve<IEyeManager>();
- _entManager = IoCManager.Resolve<IEntityManager>();
- _stationUid = mapUid;
+ NoServerLabel.Visible = true;
+ return;
+ }
+
+ NoServerLabel.Visible = false;
+
+ // Order sensor data
+ var orderedSensors = sensors.OrderBy(n => n.Name).OrderBy(j => j.Job);
+ var assignedSensors = new HashSet<SuitSensorStatus>();
+ var departments = sensors.SelectMany(d => d.JobDepartments).Distinct().OrderBy(n => n);
- if (_entManager.TryGetComponent<TransformComponent>(mapUid, out var xform))
+ // Create department labels and populate lists
+ foreach (var department in departments)
+ {
+ var departmentSensors = orderedSensors.Where(d => d.JobDepartments.Contains(department));
+
+ if (departmentSensors == null || !departmentSensors.Any())
+ continue;
+
+ foreach (var sensor in departmentSensors)
+ assignedSensors.Add(sensor);
+
+ if (SensorsTable.ChildCount > 0)
{
- NavMap.MapUid = xform.GridUid;
+ var spacer = new Control()
+ {
+ SetHeight = 20,
+ };
+
+ SensorsTable.AddChild(spacer);
+ _rowsContent.Add(spacer);
}
- else
+
+ var deparmentLabel = new RichTextLabel()
{
- NavMap.Visible = false;
- SetSize = new Vector2(775, 400);
- MinSize = SetSize;
- }
+ Margin = new Thickness(10, 0),
+ HorizontalExpand = true,
+ };
+
+ deparmentLabel.SetMessage(department);
+ deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
+
+ SensorsTable.AddChild(deparmentLabel);
+ _rowsContent.Add(deparmentLabel);
+
+ PopulateDepartmentList(departmentSensors);
}
- public void ShowSensors(List<SuitSensorStatus> stSensors, EntityCoordinates? monitorCoords, bool snap, float precision)
+ // Account for any non-station users
+ var remainingSensors = orderedSensors.Except(assignedSensors);
+
+ if (remainingSensors.Any())
{
- ClearAllSensors();
+ var spacer = new Control()
+ {
+ SetHeight = 20,
+ };
- var monitorCoordsInStationSpace = _stationUid != null ? monitorCoords?.WithEntityId(_stationUid.Value, _entManager).Position : null;
+ SensorsTable.AddChild(spacer);
+ _rowsContent.Add(spacer);
- // TODO scroll container
- // TODO filter by name & occupation
- // TODO make each row a xaml-control. Get rid of some of this c# control creation.
- if (stSensors.Count == 0)
+ var deparmentLabel = new RichTextLabel()
{
- NoServerLabel.Visible = true;
- return;
- }
- NoServerLabel.Visible = false;
+ Margin = new Thickness(10, 0),
+ HorizontalExpand = true,
+ };
+
+ deparmentLabel.SetMessage(Loc.GetString("crew-monitoring-user-interface-no-department"));
+ deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
- // add a row for each sensor
- foreach (var sensor in stSensors.OrderBy(a => a.Name))
+ SensorsTable.AddChild(deparmentLabel);
+ _rowsContent.Add(deparmentLabel);
+
+ PopulateDepartmentList(remainingSensors);
+ }
+
+ // Show monitor on nav map
+ if (monitorCoords != null && _blipTexture != null)
+ {
+ NavMap.TrackedEntities[_entManager.GetNetEntity(monitor)] = new NavMapBlip(monitorCoords.Value, _blipTexture, Color.Cyan, true, false);
+ }
+ }
+
+ private void PopulateDepartmentList(IEnumerable<SuitSensorStatus> departmentSensors)
+ {
+ // Populate departments
+ foreach (var sensor in departmentSensors)
+ {
+ var coordinates = _entManager.GetCoordinates(sensor.Coordinates);
+
+ // Add a button that will hold a username and other details
+ NavMap.LocalizedNames.TryAdd(sensor.SuitSensorUid, sensor.Name + ", " + sensor.Job);
+
+ var sensorButton = new CrewMonitoringButton()
{
- var sensorEntity = _entManager.GetEntity(sensor.SuitSensorUid);
- var coordinates = _entManager.GetCoordinates(sensor.Coordinates);
+ SuitSensorUid = sensor.SuitSensorUid,
+ Coordinates = coordinates,
+ Disabled = (coordinates == null),
+ HorizontalExpand = true,
+ };
- // add button with username
- var nameButton = new CrewMonitoringButton()
- {
- SuitSensorUid = sensorEntity,
- Coordinates = coordinates,
- Text = sensor.Name,
- Margin = new Thickness(5f, 5f),
- };
- if (sensorEntity == _trackedButton?.SuitSensorUid)
- nameButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
- SetColorLabel(nameButton.Label, sensor.TotalDamage, sensor.IsAlive);
- SensorsTable.AddChild(nameButton);
- _rowsContent.Add(nameButton);
-
- // add users job
- // format: JobName
- var jobLabel = new Label()
- {
- Text = sensor.Job,
- HorizontalExpand = true
- };
- SetColorLabel(jobLabel, sensor.TotalDamage, sensor.IsAlive);
- SensorsTable.AddChild(jobLabel);
- _rowsContent.Add(jobLabel);
-
- // add users status and damage
- // format: IsAlive (TotalDamage)
- var statusText = Loc.GetString(sensor.IsAlive ?
- "crew-monitoring-user-interface-alive" :
- "crew-monitoring-user-interface-dead");
- if (sensor.TotalDamage != null)
- {
- statusText += $" ({sensor.TotalDamage})";
- }
- var statusLabel = new Label()
- {
- Text = statusText
- };
- SetColorLabel(statusLabel, sensor.TotalDamage, sensor.IsAlive);
- SensorsTable.AddChild(statusLabel);
- _rowsContent.Add(statusLabel);
+ if (sensor.SuitSensorUid == _trackedEntity)
+ sensorButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
- // add users positions
- // format: (x, y)
- var box = GetPositionBox(sensor, monitorCoordsInStationSpace ?? Vector2.Zero, snap, precision);
+ SensorsTable.AddChild(sensorButton);
+ _rowsContent.Add(sensorButton);
- SensorsTable.AddChild(box);
- _rowsContent.Add(box);
+ // Primary container to hold the button UI elements
+ var mainContainer = new BoxContainer()
+ {
+ Orientation = LayoutOrientation.Horizontal,
+ HorizontalExpand = true,
+ };
- if (coordinates != null && NavMap.Visible)
- {
- NavMap.TrackedCoordinates.TryAdd(coordinates.Value,
- (true, sensorEntity == _trackedButton?.SuitSensorUid ? StyleNano.PointGreen : StyleNano.PointRed));
+ sensorButton.AddChild(mainContainer);
- nameButton.OnButtonUp += args =>
- {
- if (_trackedButton != null && _trackedButton?.Coordinates != null)
- //Make previous point red
- NavMap.TrackedCoordinates[_trackedButton.Coordinates.Value] = (true, StyleNano.PointRed);
+ // User status container
+ var statusContainer = new BoxContainer()
+ {
+ SizeFlagsStretchRatio = 1.25f,
+ Orientation = LayoutOrientation.Horizontal,
+ HorizontalExpand = true,
+ };
- NavMap.TrackedCoordinates[coordinates.Value] = (true, StyleNano.PointGreen);
- NavMap.CenterToCoordinates(coordinates.Value);
+ mainContainer.AddChild(statusContainer);
- nameButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
- if (_trackedButton != null)
- { //Make previous button default
- var previosButton = SensorsTable.GetChild(_trackedButton.IndexInTable);
- previosButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
- }
- _trackedButton = nameButton;
- _trackedButton.IndexInTable = nameButton.GetPositionInParent();
- };
- }
- }
- // Show monitor point
- if (monitorCoords != null)
- NavMap.TrackedCoordinates.Add(monitorCoords.Value, (true, StyleNano.PointMagenta));
- }
+ // Suit coords indicator
+ var suitCoordsIndicator = new TextureRect()
+ {
+ Texture = _blipTexture,
+ TextureScale = new Vector2(0.25f, 0.25f),
+ Modulate = coordinates != null ? Color.LimeGreen : Color.DarkRed,
+ HorizontalAlignment = HAlignment.Center,
+ VerticalAlignment = VAlignment.Center,
+ };
- private BoxContainer GetPositionBox(SuitSensorStatus sensor, Vector2 monitorCoordsInStationSpace, bool snap, float precision)
- {
- EntityCoordinates? coordinates = _entManager.GetCoordinates(sensor.Coordinates);
- var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal };
+ statusContainer.AddChild(suitCoordsIndicator);
+
+ // Specify texture for the user status icon
+ var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "alive");
- if (coordinates == null || _stationUid == null)
+ if (!sensor.IsAlive)
{
- var dirIcon = new DirectionIcon()
- {
- SetSize = new Vector2(IconSize, IconSize),
- Margin = new(0, 0, 4, 0)
- };
- box.AddChild(dirIcon);
- box.AddChild(new Label() { Text = Loc.GetString("crew-monitoring-user-interface-no-info") });
+ specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead");
}
- else
+
+ else if (sensor.TotalDamage != null)
{
- var local = coordinates.Value.WithEntityId(_stationUid.Value, _entManager).Position;
+ var index = MathF.Round(4f * (sensor.TotalDamage.Value / 100f));
- var displayPos = local.Floored();
- var dirIcon = new DirectionIcon(snap, precision)
- {
- SetSize = new Vector2(IconSize, IconSize),
- Margin = new(0, 0, 4, 0)
- };
- box.AddChild(dirIcon);
- Label label = new Label() { Text = displayPos.ToString() };
- SetColorLabel(label, sensor.TotalDamage, sensor.IsAlive);
- box.AddChild(label);
- _directionIcons.Add((dirIcon, local - monitorCoordsInStationSpace));
+ if (index >= 5)
+ specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
+
+ else
+ specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "health" + index);
}
- return box;
- }
+ // Status icon
+ var statusIcon = new AnimatedTextureRect
+ {
+ HorizontalAlignment = HAlignment.Center,
+ VerticalAlignment = VAlignment.Center,
+ Margin = new Thickness(0, 1, 3, 0),
+ };
- protected override void FrameUpdate(FrameEventArgs args)
- {
- // the window is separate from any specific viewport, so there is no real way to get an eye-rotation without
- // using IEyeManager. Eventually this will have to be reworked for a station AI with multi-viewports.
- // (From the future: Or alternatively, just disable the angular offset for station AIs?)
-
- // An offsetAngle of zero here perfectly aligns directions to the station map.
- // Note that the "relative angle" does this weird inverse-inverse thing.
- // Could recalculate it all in world coordinates and then pass in eye directly... or do this.
- var offsetAngle = Angle.Zero;
- if (_entManager.TryGetComponent<TransformComponent>(_stationUid, out var xform))
+ statusIcon.SetFromSpriteSpecifier(specifier);
+ statusIcon.DisplayRect.TextureScale = new Vector2(2f, 2f);
+
+ statusContainer.AddChild(statusIcon);
+
+ // User name
+ var nameLabel = new Label()
+ {
+ Text = sensor.Name,
+ HorizontalExpand = true,
+ ClipText = true,
+ };
+
+ statusContainer.AddChild(nameLabel);
+
+ // User job container
+ var jobContainer = new BoxContainer()
+ {
+ Orientation = LayoutOrientation.Horizontal,
+ HorizontalExpand = true,
+ };
+
+ mainContainer.AddChild(jobContainer);
+
+ // Job icon
+ if (_prototypeManager.TryIndex<StatusIconPrototype>(sensor.JobIcon, out var proto))
{
- // Apply the offset relative to the eye.
- // For a station at 45 degrees rotation, the current eye rotation is -45 degrees.
- // TODO: This feels sketchy. Is there something underlying wrong with eye rotation?
- offsetAngle = -(_eye.CurrentEye.Rotation + xform.WorldRotation);
+ var jobIcon = new TextureRect()
+ {
+ TextureScale = new Vector2(2f, 2f),
+ Stretch = TextureRect.StretchMode.KeepCentered,
+ Texture = _spriteSystem.Frame0(proto.Icon),
+ Margin = new Thickness(5, 0, 5, 0),
+ };
+
+ jobContainer.AddChild(jobIcon);
}
- foreach (var (icon, pos) in _directionIcons)
+ // Job name
+ var jobLabel = new Label()
{
- icon.UpdateDirection(pos, offsetAngle);
+ Text = sensor.Job,
+ HorizontalExpand = true,
+ ClipText = true,
+ };
+
+ jobContainer.AddChild(jobLabel);
+
+ // Add user coordinates to the navmap
+ if (coordinates != null && NavMap.Visible && _blipTexture != null)
+ {
+ NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid,
+ new NavMapBlip
+ (coordinates.Value,
+ _blipTexture,
+ (_trackedEntity == null || sensor.SuitSensorUid == _trackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
+ sensor.SuitSensorUid == _trackedEntity));
+
+ NavMap.Focus = _trackedEntity;
+
+ // On button up
+ sensorButton.OnButtonUp += args =>
+ {
+ var prevTrackedEntity = _trackedEntity;
+
+ if (_trackedEntity == sensor.SuitSensorUid)
+ {
+ _trackedEntity = null;
+ }
+
+ else
+ {
+ _trackedEntity = sensor.SuitSensorUid;
+ NavMap.CenterToCoordinates(coordinates.Value);
+ }
+
+ NavMap.Focus = _trackedEntity;
+
+ UpdateSensorsTable(_trackedEntity, prevTrackedEntity);
+ };
}
}
+ }
+
+ private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
+ {
+ var prevTrackedEntity = _trackedEntity;
+ _trackedEntity = netEntity;
+
+ if (_trackedEntity == prevTrackedEntity)
+ prevTrackedEntity = null;
+
+ NavMap.Focus = _trackedEntity;
+ _tryToScrollToListFocus = true;
+
+ UpdateSensorsTable(_trackedEntity, prevTrackedEntity);
+ }
- private void ClearAllSensors()
+ private void UpdateSensorsTable(NetEntity? currTrackedEntity, NetEntity? prevTrackedEntity)
+ {
+ foreach (var sensor in SensorsTable.Children)
{
- foreach (var child in _rowsContent)
+ if (sensor is not CrewMonitoringButton)
+ continue;
+
+ var castSensor = (CrewMonitoringButton) sensor;
+
+ if (castSensor.SuitSensorUid == prevTrackedEntity)
+ castSensor.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
+
+ else if (castSensor.SuitSensorUid == currTrackedEntity)
+ castSensor.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
+
+ if (castSensor?.Coordinates == null)
+ continue;
+
+ if (NavMap.TrackedEntities.TryGetValue(castSensor.SuitSensorUid, out var data))
{
- SensorsTable.RemoveChild(child);
+ data = new NavMapBlip
+ (data.Coordinates,
+ data.Texture,
+ (currTrackedEntity == null || castSensor.SuitSensorUid == currTrackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
+ castSensor.SuitSensorUid == currTrackedEntity);
+
+ NavMap.TrackedEntities[castSensor.SuitSensorUid] = data;
}
- _rowsContent.Clear();
- _directionIcons.Clear();
- NavMap.TrackedCoordinates.Clear();
}
+ }
- private void SetColorLabel(Label label, int? totalDamage, bool isAlive)
+ private void TryToScrollToFocus()
+ {
+ if (!_tryToScrollToListFocus)
+ return;
+
+ if (!TryGetVerticalScrollbar(SensorScroller, out var vScrollbar))
+ return;
+
+ if (TryGetNextScrollPosition(out float? nextScrollPosition))
{
- var startColor = Color.White;
- var critColor = Color.Yellow;
- var endColor = Color.Red;
+ vScrollbar.ValueTarget = nextScrollPosition.Value;
- if (!isAlive)
+ if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
{
- label.FontColorOverride = endColor;
+ _tryToScrollToListFocus = false;
return;
}
+ }
+ }
- //Convert from null to regular int
- int damage;
- if (totalDamage == null) return;
- else damage = (int) totalDamage;
+ private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
+ {
+ vScrollBar = null;
- if (damage <= 0)
- {
- label.FontColorOverride = startColor;
- }
- else if (damage >= 200)
- {
- label.FontColorOverride = endColor;
- }
- else if (damage >= 0 && damage <= 100)
- {
- label.FontColorOverride = GetColorLerp(startColor, critColor, damage);
- }
- else if (damage >= 100 && damage <= 200)
- {
- //We need a number from 0 to 100. Divide the number from 100 to 200 by 2
- damage /= 2;
- label.FontColorOverride = GetColorLerp(critColor, endColor, damage);
- }
+ foreach (var child in scroll.Children)
+ {
+ if (child is not VScrollBar)
+ continue;
+
+ vScrollBar = (VScrollBar) child;
+ return true;
}
- private Color GetColorLerp(Color startColor, Color endColor, int damage)
+ return false;
+ }
+
+ private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
+ {
+ nextScrollPosition = 0;
+
+ foreach (var sensor in SensorsTable.Children)
{
- //Smooth transition from one color to another depending on the percentage
- var t = damage / 100f;
- var r = MathHelper.Lerp(startColor.R, endColor.R, t);
- var g = MathHelper.Lerp(startColor.G, endColor.G, t);
- var b = MathHelper.Lerp(startColor.B, endColor.B, t);
- var a = MathHelper.Lerp(startColor.A, endColor.A, t);
-
- return new Color(r, g, b, a);
+ if (sensor is CrewMonitoringButton &&
+ ((CrewMonitoringButton) sensor).SuitSensorUid == _trackedEntity)
+ return true;
+
+ nextScrollPosition += sensor.Height;
}
+
+ // Failed to find control
+ nextScrollPosition = null;
+
+ return false;
}
- public sealed class CrewMonitoringButton : Button
+ private void ClearOutDatedData()
{
- public int IndexInTable;
- public EntityUid? SuitSensorUid;
- public EntityCoordinates? Coordinates;
+ SensorsTable.RemoveAllChildren();
+ _rowsContent.Clear();
+ NavMap.TrackedCoordinates.Clear();
+ NavMap.TrackedEntities.Clear();
+ NavMap.LocalizedNames.Clear();
}
}
+
+public sealed class CrewMonitoringButton : Button
+{
+ public int IndexInTable;
+ public NetEntity SuitSensorUid;
+ public EntityCoordinates? Coordinates;
+}
-using System.Numerics;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Pinpointer;
-using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Collections;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
+using Robust.Shared.Timing;
+using System.Numerics;
+using JetBrains.Annotations;
namespace Content.Client.Pinpointer.UI;
/// <summary>
/// Displays the nav map data of the specified grid.
/// </summary>
-public sealed class NavMapControl : MapGridControl
+[UsedImplicitly, Virtual]
+public partial class NavMapControl : MapGridControl
{
[Dependency] private readonly IEntityManager _entManager = default!;
- private SharedTransformSystem _transform;
+ private readonly SharedTransformSystem _transformSystem = default!;
public EntityUid? MapUid;
+ // Actions
+ public event Action<NetEntity?>? TrackedEntitySelectedAction;
+
+ // Tracked data
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
+ public Dictionary<NetEntity, NavMapBlip> TrackedEntities = new();
+ public Dictionary<Vector2i, List<NavMapLine>> TileGrid = default!;
+
+ // Default colors
+ public Color WallColor = new(102, 217, 102);
+ public Color TileColor = new(30, 67, 30);
+
+ // Constants
+ protected float UpdateTime = 1.0f;
+ protected float MaxSelectableDistance = 10f;
+ protected float RecenterMinimum = 0.05f;
+ // Local variables
private Vector2 _offset;
private bool _draggin;
private bool _recentering = false;
- private readonly float _recenterMinimum = 0.05f;
private readonly Font _font;
- private static readonly Color TileColor = new(30, 67, 30);
- private static readonly Color BeaconColor = Color.FromSrgb(TileColor.WithAlpha(0.8f));
+ private float _updateTimer = 0.25f;
+ private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
+ private Color _beaconColor;
+
+ // Components
+ private NavMapComponent? _navMap;
+ private MapGridComponent? _grid;
+ private TransformComponent? _xform;
+ private PhysicsComponent? _physics;
+ private FixturesComponent? _fixtures;
// TODO: https://github.com/space-wizards/RobustToolbox/issues/3818
private readonly Label _zoom = new()
private readonly Button _recenter = new()
{
- Text = "Recentre",
+ Text = Loc.GetString("navmap-recenter"),
VerticalAlignment = VAlignment.Top,
HorizontalAlignment = HAlignment.Right,
Margin = new Thickness(8f, 4f),
Disabled = true,
};
+ private readonly CheckBox _beacons = new()
+ {
+ Text = Loc.GetString("navmap-toggle-beacons"),
+ Margin = new Thickness(4f, 0f),
+ VerticalAlignment = VAlignment.Center,
+ HorizontalAlignment = HAlignment.Center,
+ Pressed = false,
+ };
+
public NavMapControl() : base(8f, 128f, 48f)
{
IoCManager.InjectDependencies(this);
-
- _transform = _entManager.System<SharedTransformSystem>();
var cache = IoCManager.Resolve<IResourceCache>();
- _font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 16);
+
+ _transformSystem = _entManager.System<SharedTransformSystem>();
+ _font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 12);
+ _beaconColor = Color.FromSrgb(TileColor.WithAlpha(0.8f));
RectClipContent = true;
HorizontalExpand = true;
Children =
{
_zoom,
+ _beacons,
_recenter,
}
};
{
_recentering = true;
};
+
+ ForceNavMapUpdate();
+ }
+
+ public void ForceRecenter()
+ {
+ _recentering = true;
+ }
+
+ public void ForceNavMapUpdate()
+ {
+ _entManager.TryGetComponent(MapUid, out _navMap);
+ _entManager.TryGetComponent(MapUid, out _grid);
+
+ UpdateNavMap();
}
public void CenterToCoordinates(EntityCoordinates coordinates)
{
- if (_entManager.TryGetComponent<PhysicsComponent>(MapUid, out var physics))
- {
- _offset = new Vector2(coordinates.X, coordinates.Y) - physics.LocalCenter;
- }
+ if (_physics != null)
+ _offset = new Vector2(coordinates.X, coordinates.Y) - _physics.LocalCenter;
+
_recenter.Disabled = false;
}
base.KeyBindDown(args);
if (args.Function == EngineKeyFunctions.Use)
- {
_draggin = true;
- }
}
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
base.KeyBindUp(args);
+ if (TrackedEntitySelectedAction == null)
+ return;
+
if (args.Function == EngineKeyFunctions.Use)
{
_draggin = false;
+
+ if (_xform == null || _physics == null || TrackedEntities.Count == 0)
+ return;
+
+ // Get the clicked position
+ var offset = _offset + _physics.LocalCenter;
+ var localPosition = args.PointerLocation.Position - GlobalPixelPosition;
+
+ // Convert to a world position
+ var unscaledPosition = (localPosition - MidpointVector) / MinimapScale;
+ var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset);
+
+ // Find closest tracked entity in range
+ var closestEntity = NetEntity.Invalid;
+ var closestCoords = new EntityCoordinates();
+ var closestDistance = float.PositiveInfinity;
+
+ foreach ((var currentEntity, var blip) in TrackedEntities)
+ {
+ if (!blip.Selectable)
+ continue;
+
+ var currentDistance = (blip.Coordinates.ToMapPos(_entManager, _transformSystem) - worldPosition).Length();
+
+ if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance)
+ continue;
+
+ closestEntity = currentEntity;
+ closestCoords = blip.Coordinates;
+ closestDistance = currentDistance;
+ }
+
+ if (closestDistance > MaxSelectableDistance || !closestEntity.IsValid())
+ return;
+
+ TrackedEntitySelectedAction.Invoke(closestEntity);
+ }
+
+ else if (args.Function == EngineKeyFunctions.UIRightClick)
+ {
+ // Clear current selection with right click
+ if (TrackedEntitySelectedAction != null)
+ TrackedEntitySelectedAction.Invoke(null);
}
}
_offset -= new Vector2(args.Relative.X, -args.Relative.Y) / MidPoint * WorldRange;
if (_offset != Vector2.Zero)
- {
_recenter.Disabled = false;
- }
+
else
- {
_recenter.Disabled = true;
- }
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
+ // Get the components necessary for drawing the navmap
+ _entManager.TryGetComponent(MapUid, out _navMap);
+ _entManager.TryGetComponent(MapUid, out _grid);
+ _entManager.TryGetComponent(MapUid, out _xform);
+ _entManager.TryGetComponent(MapUid, out _physics);
+ _entManager.TryGetComponent(MapUid, out _fixtures);
+
+ // Map re-centering
if (_recentering)
{
var frameTime = Timing.FrameTime;
var diff = _offset * (float) frameTime.TotalSeconds;
- if (_offset.LengthSquared() < _recenterMinimum)
+ if (_offset.LengthSquared() < RecenterMinimum)
{
_offset = Vector2.Zero;
_recentering = false;
}
}
- _zoom.Text = $"Zoom: {(WorldRange / WorldMaxRange * 100f):0.00}%";
+ _zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(WorldRange / WorldMaxRange * 100f):0.00}"));
- if (!_entManager.TryGetComponent<NavMapComponent>(MapUid, out var navMap) ||
- !_entManager.TryGetComponent<TransformComponent>(MapUid, out var xform) ||
- !_entManager.TryGetComponent<MapGridComponent>(MapUid, out var grid))
- {
+ if (_navMap == null || _xform == null)
return;
- }
var offset = _offset;
- var lineColor = new Color(102, 217, 102);
- if (_entManager.TryGetComponent<PhysicsComponent>(MapUid, out var physics))
- {
- offset += physics.LocalCenter;
- }
+ if (_physics != null)
+ offset += _physics.LocalCenter;
// Draw tiles
- if (_entManager.TryGetComponent<FixturesComponent>(MapUid, out var manager))
+ if (_fixtures != null)
{
Span<Vector2> verts = new Vector2[8];
- foreach (var fixture in manager.Fixtures.Values)
+ foreach (var fixture in _fixtures.Fixtures.Values)
{
if (fixture.Shape is not PolygonShape poly)
continue;
}
}
- // Draw the wall data
var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset);
- var tileSize = new Vector2(grid.TileSize, -grid.TileSize);
- for (var x = Math.Floor(area.Left); x <= Math.Ceiling(area.Right); x += SharedNavMapSystem.ChunkSize * grid.TileSize)
+ // Drawing lines can be rather expensive due to the number of neighbors that need to be checked in order
+ // to figure out where they should be drawn. However, we don't *need* to do check these every frame.
+ // Instead, lets periodically update where to draw each line and then store these points in a list.
+ // Then we can just run through the list each frame and draw the lines without any extra computation.
+
+ // Draw walls
+ if (TileGrid != null && TileGrid.Count > 0)
{
- for (var y = Math.Floor(area.Bottom); y <= Math.Ceiling(area.Top); y += SharedNavMapSystem.ChunkSize * grid.TileSize)
+ var walls = new ValueList<Vector2>();
+
+ foreach ((var chunk, var chunkedLines) in TileGrid)
{
- var floored = new Vector2i((int) x, (int) y);
+ var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
- var chunkOrigin = SharedMapSystem.GetChunkIndices(floored, SharedNavMapSystem.ChunkSize);
+ if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
+ continue;
- if (!navMap.Chunks.TryGetValue(chunkOrigin, out var chunk))
+ if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
continue;
- // TODO: Okay maybe I should just use ushorts lmao...
- for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
+ foreach (var chunkedLine in chunkedLines)
{
- var value = (int) Math.Pow(2, i);
-
- var mask = chunk.TileData & value;
-
- if (mask == 0x0)
- continue;
-
- // Alright now we'll work out our edges
- var relativeTile = SharedNavMapSystem.GetTile(mask);
- var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize - offset;
- var position = new Vector2(tile.X, -tile.Y);
- NavMapChunk? neighborChunk;
- bool neighbor;
-
- // North edge
- if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
- {
- neighbor = navMap.Chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
- (neighborChunk.TileData &
- SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
- }
- else
- {
- var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
- neighbor = (chunk.TileData & flag) != 0x0;
- }
-
- if (!neighbor)
- {
- handle.DrawLine(Scale(position + new Vector2(0f, -grid.TileSize)), Scale(position + tileSize), lineColor);
- }
-
- // East edge
- if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
- {
- neighbor = navMap.Chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
- (neighborChunk.TileData &
- SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
- }
- else
- {
- var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
- neighbor = (chunk.TileData & flag) != 0x0;
- }
-
- if (!neighbor)
- {
- handle.DrawLine(Scale(position + tileSize), Scale(position + new Vector2(grid.TileSize, 0f)), lineColor);
- }
-
- // South edge
- if (relativeTile.Y == 0)
- {
- neighbor = navMap.Chunks.TryGetValue(chunkOrigin + new Vector2i(0, -1), out neighborChunk) &&
- (neighborChunk.TileData &
- SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
- }
- else
- {
- var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
- neighbor = (chunk.TileData & flag) != 0x0;
- }
-
- if (!neighbor)
- {
- handle.DrawLine(Scale(position + new Vector2(grid.TileSize, 0f)), Scale(position), lineColor);
- }
-
- // West edge
- if (relativeTile.X == 0)
- {
- neighbor = navMap.Chunks.TryGetValue(chunkOrigin + new Vector2i(-1, 0), out neighborChunk) &&
- (neighborChunk.TileData &
- SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
- }
- else
- {
- var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
- neighbor = (chunk.TileData & flag) != 0x0;
- }
-
- if (!neighbor)
- {
- handle.DrawLine(Scale(position), Scale(position + new Vector2(0f, -grid.TileSize)), lineColor);
- }
-
- // Draw a diagonal line for interiors.
- handle.DrawLine(Scale(position + new Vector2(0f, -grid.TileSize)), Scale(position + new Vector2(grid.TileSize, 0f)), lineColor);
+ var start = Scale(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
+ var end = Scale(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
+
+ walls.Add(start);
+ walls.Add(end);
}
}
+
+ if (walls.Count > 0)
+ {
+ if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB))
+ {
+ sRGB = Color.ToSrgb(WallColor);
+ _sRGBLookUp[WallColor] = sRGB;
+ }
+
+ handle.DrawPrimitives(DrawPrimitiveTopology.LineList, walls.Span, sRGB);
+ }
+ }
+
+ // Beacons
+ if (_beacons.Pressed)
+ {
+ var rectBuffer = new Vector2(5f, 3f);
+
+ foreach (var beacon in _navMap.Beacons)
+ {
+ var position = beacon.Position - offset;
+ position = Scale(position with { Y = -position.Y });
+
+ var textDimensions = handle.GetDimensions(_font, beacon.Text, 1f);
+ handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), _beaconColor);
+ handle.DrawString(_font, position - textDimensions / 2, beacon.Text, beacon.Color);
+ }
}
var curTime = Timing.RealTime;
var blinkFrequency = 1f / 1f;
var lit = curTime.TotalSeconds % blinkFrequency > blinkFrequency / 2f;
+ // Tracked coordinates (simple dot, legacy)
foreach (var (coord, value) in TrackedCoordinates)
{
if (lit && value.Visible)
{
- var mapPos = coord.ToMap(_entManager);
+ var mapPos = coord.ToMap(_entManager, _transformSystem);
if (mapPos.MapId != MapId.Nullspace)
{
- var position = xform.InvWorldMatrix.Transform(mapPos.Position) - offset;
+ var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
position = Scale(new Vector2(position.X, -position.Y));
- handle.DrawCircle(position, MinimapScale / 2f, value.Color);
+ handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color);
}
}
}
- // Beacons
- var labelOffset = new Vector2(0.5f, 0.5f) * MinimapScale;
- var rectBuffer = new Vector2(5f, 3f);
+ // Tracked entities (can use a supplied sprite as a marker instead; should probably just replace TrackedCoordinates with this eventually)
+ var iconVertexUVs = new Dictionary<(Texture, Color), ValueList<DrawVertexUV2D>>();
- foreach (var beacon in navMap.Beacons)
+ foreach (var blip in TrackedEntities.Values)
{
- var position = beacon.Position - offset;
+ if (blip.Blinks && !lit)
+ continue;
+
+ if (blip.Texture == null)
+ continue;
- position = Scale(position with { Y = -position.Y });
+ if (!iconVertexUVs.TryGetValue((blip.Texture, blip.Color), out var vertexUVs))
+ vertexUVs = new();
- handle.DrawCircle(position, MinimapScale / 2f, beacon.Color);
- var textDimensions = handle.GetDimensions(_font, beacon.Text, 1f);
+ var mapPos = blip.Coordinates.ToMap(_entManager, _transformSystem);
- var labelPosition = position + labelOffset;
- handle.DrawRect(new UIBox2(labelPosition, labelPosition + textDimensions + rectBuffer * 2), BeaconColor);
- handle.DrawString(_font, labelPosition + rectBuffer, beacon.Text, beacon.Color);
+ if (mapPos.MapId != MapId.Nullspace)
+ {
+ var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
+ position = Scale(new Vector2(position.X, -position.Y));
+
+ var scalingCoefficient = 2.5f;
+ var positionOffset = scalingCoefficient * float.Sqrt(MinimapScale);
+
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y - positionOffset), new Vector2(1f, 1f)));
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
+ vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y + positionOffset), new Vector2(0f, 0f)));
+ }
+
+ iconVertexUVs[(blip.Texture, blip.Color)] = vertexUVs;
}
+
+ foreach ((var (texture, color), var vertexUVs) in iconVertexUVs)
+ {
+ if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
+ {
+ sRGB = Color.ToSrgb(color);
+ _sRGBLookUp[color] = sRGB;
+ }
+
+ handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, texture, vertexUVs.Span, sRGB);
+ }
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ // Update the timer
+ _updateTimer += args.DeltaSeconds;
+
+ if (_updateTimer >= UpdateTime)
+ {
+ _updateTimer -= UpdateTime;
+
+ UpdateNavMap();
+ }
+ }
+
+ private void UpdateNavMap()
+ {
+ if (_navMap == null || _grid == null)
+ return;
+
+ TileGrid = GetDecodedWallChunks(_navMap.Chunks, _grid);
+ }
+
+ public Dictionary<Vector2i, List<NavMapLine>> GetDecodedWallChunks
+ (Dictionary<Vector2i, NavMapChunk> chunks,
+ MapGridComponent grid)
+ {
+ var decodedOutput = new Dictionary<Vector2i, List<NavMapLine>>();
+
+ foreach ((var chunkOrigin, var chunk) in chunks)
+ {
+ var list = new List<NavMapLine>();
+
+ // TODO: Okay maybe I should just use ushorts lmao...
+ for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
+ {
+ var value = (int) Math.Pow(2, i);
+
+ var mask = chunk.TileData & value;
+
+ if (mask == 0x0)
+ continue;
+
+ // Alright now we'll work out our edges
+ var relativeTile = SharedNavMapSystem.GetTile(mask);
+ var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize;
+ var position = new Vector2(tile.X, -tile.Y);
+ NavMapChunk? neighborChunk;
+ bool neighbor;
+
+ // North edge
+ if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
+ {
+ neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
+ (neighborChunk.TileData &
+ SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
+ }
+ else
+ {
+ var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
+ neighbor = (chunk.TileData & flag) != 0x0;
+ }
+
+ if (!neighbor)
+ {
+ // Add points
+ list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, -grid.TileSize)));
+ }
+
+ // East edge
+ if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
+ {
+ neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
+ (neighborChunk.TileData &
+ SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
+ }
+ else
+ {
+ var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
+ neighbor = (chunk.TileData & flag) != 0x0;
+ }
+
+ if (!neighbor)
+ {
+ // Add points
+ list.Add(new NavMapLine(position + new Vector2(grid.TileSize, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
+ }
+
+ // South edge
+ if (relativeTile.Y == 0)
+ {
+ neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, -1), out neighborChunk) &&
+ (neighborChunk.TileData &
+ SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
+ }
+ else
+ {
+ var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
+ neighbor = (chunk.TileData & flag) != 0x0;
+ }
+
+ if (!neighbor)
+ {
+ // Add points
+ list.Add(new NavMapLine(position + new Vector2(grid.TileSize, 0f), position));
+ }
+
+ // West edge
+ if (relativeTile.X == 0)
+ {
+ neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(-1, 0), out neighborChunk) &&
+ (neighborChunk.TileData &
+ SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
+ }
+ else
+ {
+ var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
+ neighbor = (chunk.TileData & flag) != 0x0;
+ }
+
+ if (!neighbor)
+ {
+ // Add point
+ list.Add(new NavMapLine(position, position + new Vector2(0f, -grid.TileSize)));
+ }
+
+ // Draw a diagonal line for interiors.
+ list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
+ }
+
+ decodedOutput.Add(chunkOrigin, list);
+ }
+
+ return decodedOutput;
}
- private Vector2 Scale(Vector2 position)
+ protected Vector2 Scale(Vector2 position)
{
return position * MinimapScale + MidpointVector;
}
+
+ protected Vector2 GetOffset()
+ {
+ return _offset + (_physics != null ? _physics.LocalCenter : new Vector2());
+ }
+}
+
+public struct NavMapBlip
+{
+ public EntityCoordinates Coordinates;
+ public Texture Texture;
+ public Color Color;
+ public bool Blinks;
+ public bool Selectable;
+
+ public NavMapBlip(EntityCoordinates coordinates, Texture texture, Color color, bool blinks, bool selectable = true)
+ {
+ Coordinates = coordinates;
+ Texture = texture;
+ Color = color;
+ Blinks = blinks;
+ Selectable = selectable;
+ }
+}
+
+public struct NavMapLine
+{
+ public readonly Vector2 Origin;
+ public readonly Vector2 Terminus;
+
+ public NavMapLine(Vector2 origin, Vector2 terminus)
+ {
+ Origin = origin;
+ Terminus = terminus;
+ }
}
{
Title = metadata.EntityName;
}
+
+ NavMapScreen.ForceNavMapUpdate();
}
}
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
-namespace Content.Server.Access.Components
+namespace Content.Server.Access.Components;
+
+[RegisterComponent]
+public sealed partial class PresetIdCardComponent : Component
{
- [RegisterComponent]
- public sealed partial class PresetIdCardComponent : Component
- {
- [DataField("job")]
- public ProtoId<JobPrototype>? JobName;
+ [DataField("job")]
+ public ProtoId<JobPrototype>? JobName;
- [DataField("name")]
- public string? IdName;
- }
+ [DataField("name")]
+ public string? IdName;
}
using Content.Shared.Access.Systems;
using Content.Shared.Database;
using Content.Shared.Popups;
+using Content.Shared.Roles;
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-namespace Content.Server.Access.Systems
+namespace Content.Server.Access.Systems;
+
+public sealed class IdCardSystem : SharedIdCardSystem
{
- public sealed class IdCardSystem : SharedIdCardSystem
- {
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly MetaDataSystem _metaSystem = default!;
+ [Dependency] private readonly PopupSystem _popupSystem = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly MetaDataSystem _metaSystem = default!;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<IdCardComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<IdCardComponent, BeingMicrowavedEvent>(OnMicrowaved);
- }
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<IdCardComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<IdCardComponent, BeingMicrowavedEvent>(OnMicrowaved);
+ }
- private void OnMapInit(EntityUid uid, IdCardComponent id, MapInitEvent args)
- {
- UpdateEntityName(uid, id);
- }
+ private void OnMapInit(EntityUid uid, IdCardComponent id, MapInitEvent args)
+ {
+ UpdateEntityName(uid, id);
+ }
- private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
+ private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
+ {
+ if (TryComp<AccessComponent>(uid, out var access))
{
- if (TryComp<AccessComponent>(uid, out var access))
+ float randomPick = _random.NextFloat();
+ // if really unlucky, burn card
+ if (randomPick <= 0.15f)
{
- float randomPick = _random.NextFloat();
- // if really unlucky, burn card
- if (randomPick <= 0.15f)
+ TryComp(uid, out TransformComponent? transformComponent);
+ if (transformComponent != null)
{
- TryComp(uid, out TransformComponent? transformComponent);
- if (transformComponent != null)
- {
- _popupSystem.PopupCoordinates(Loc.GetString("id-card-component-microwave-burnt", ("id", uid)),
- transformComponent.Coordinates, PopupType.Medium);
- EntityManager.SpawnEntity("FoodBadRecipe",
- transformComponent.Coordinates);
- }
- _adminLogger.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(args.Microwave)} burnt {ToPrettyString(uid):entity}");
- EntityManager.QueueDeleteEntity(uid);
- return;
+ _popupSystem.PopupCoordinates(Loc.GetString("id-card-component-microwave-burnt", ("id", uid)),
+ transformComponent.Coordinates, PopupType.Medium);
+ EntityManager.SpawnEntity("FoodBadRecipe",
+ transformComponent.Coordinates);
}
- // If they're unlucky, brick their ID
- if (randomPick <= 0.25f)
- {
- _popupSystem.PopupEntity(Loc.GetString("id-card-component-microwave-bricked", ("id", uid)), uid);
-
- access.Tags.Clear();
- Dirty(access);
-
- _adminLogger.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(args.Microwave)} cleared access on {ToPrettyString(uid):entity}");
- }
- else
- {
- _popupSystem.PopupEntity(Loc.GetString("id-card-component-microwave-safe", ("id", uid)), uid, PopupType.Medium);
- }
-
- // Give them a wonderful new access to compensate for everything
- var random = _random.Pick(_prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().ToArray());
-
- access.Tags.Add(random.ID);
- Dirty(access);
-
_adminLogger.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(args.Microwave)} added {random.ID} access to {ToPrettyString(uid):entity}");
+ $"{ToPrettyString(args.Microwave)} burnt {ToPrettyString(uid):entity}");
+ EntityManager.QueueDeleteEntity(uid);
+ return;
}
- }
-
- /// <summary>
- /// Attempts to change the job title of a card.
- /// Returns true/false.
- /// </summary>
- /// <remarks>
- /// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
- /// </remarks>
- public bool TryChangeJobTitle(EntityUid uid, string? jobTitle, IdCardComponent? id = null, EntityUid? player = null)
- {
- if (!Resolve(uid, ref id))
- return false;
-
- if (!string.IsNullOrWhiteSpace(jobTitle))
+ // If they're unlucky, brick their ID
+ if (randomPick <= 0.25f)
{
- jobTitle = jobTitle.Trim();
+ _popupSystem.PopupEntity(Loc.GetString("id-card-component-microwave-bricked", ("id", uid)), uid);
- if (jobTitle.Length > IdCardConsoleComponent.MaxJobTitleLength)
- jobTitle = jobTitle[..IdCardConsoleComponent.MaxJobTitleLength];
+ access.Tags.Clear();
+ Dirty(access);
+
+ _adminLogger.Add(LogType.Action, LogImpact.Medium,
+ $"{ToPrettyString(args.Microwave)} cleared access on {ToPrettyString(uid):entity}");
}
else
{
- jobTitle = null;
+ _popupSystem.PopupEntity(Loc.GetString("id-card-component-microwave-safe", ("id", uid)), uid, PopupType.Medium);
}
- if (id.JobTitle == jobTitle)
- return true;
- id.JobTitle = jobTitle;
- Dirty(id);
- UpdateEntityName(uid, id);
+ // Give them a wonderful new access to compensate for everything
+ var random = _random.Pick(_prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().ToArray());
- if (player != null)
- {
- _adminLogger.Add(LogType.Identity, LogImpact.Low,
- $"{ToPrettyString(player.Value):player} has changed the job title of {ToPrettyString(uid):entity} to {jobTitle} ");
- }
- return true;
+ access.Tags.Add(random.ID);
+ Dirty(access);
+
+ _adminLogger.Add(LogType.Action, LogImpact.Medium,
+ $"{ToPrettyString(args.Microwave)} added {random.ID} access to {ToPrettyString(uid):entity}");
}
+ }
+
+ /// <summary>
+ /// Attempts to change the job title of a card.
+ /// Returns true/false.
+ /// </summary>
+ /// <remarks>
+ /// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
+ /// </remarks>
+ public bool TryChangeJobTitle(EntityUid uid, string? jobTitle, IdCardComponent? id = null, EntityUid? player = null)
+ {
+ if (!Resolve(uid, ref id))
+ return false;
- public bool TryChangeJobIcon(EntityUid uid, StatusIconPrototype jobIcon, IdCardComponent? id = null, EntityUid? player = null)
+ if (!string.IsNullOrWhiteSpace(jobTitle))
{
- if (!Resolve(uid, ref id))
- {
- return false;
- }
+ jobTitle = jobTitle.Trim();
- if (id.JobIcon == jobIcon.ID)
- {
- return true;
- }
+ if (jobTitle.Length > IdCardConsoleComponent.MaxJobTitleLength)
+ jobTitle = jobTitle[..IdCardConsoleComponent.MaxJobTitleLength];
+ }
+ else
+ {
+ jobTitle = null;
+ }
- id.JobIcon = jobIcon.ID;
- Dirty(uid, id);
+ if (id.JobTitle == jobTitle)
+ return true;
+ id.JobTitle = jobTitle;
+ Dirty(id);
+ UpdateEntityName(uid, id);
- if (player != null)
- {
- _adminLogger.Add(LogType.Identity, LogImpact.Low,
- $"{ToPrettyString(player.Value):player} has changed the job icon of {ToPrettyString(uid):entity} to {jobIcon} ");
- }
+ if (player != null)
+ {
+ _adminLogger.Add(LogType.Identity, LogImpact.Low,
+ $"{ToPrettyString(player.Value):player} has changed the job title of {ToPrettyString(uid):entity} to {jobTitle} ");
+ }
+ return true;
+ }
+
+ public bool TryChangeJobIcon(EntityUid uid, StatusIconPrototype jobIcon, IdCardComponent? id = null, EntityUid? player = null)
+ {
+ if (!Resolve(uid, ref id))
+ {
+ return false;
+ }
+ if (id.JobIcon == jobIcon.ID)
+ {
return true;
}
- /// <summary>
- /// Attempts to change the full name of a card.
- /// Returns true/false.
- /// </summary>
- /// <remarks>
- /// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
- /// </remarks>
- public bool TryChangeFullName(EntityUid uid, string? fullName, IdCardComponent? id = null, EntityUid? player = null)
+ id.JobIcon = jobIcon.ID;
+ Dirty(uid, id);
+
+ if (player != null)
{
- if (!Resolve(uid, ref id))
- return false;
+ _adminLogger.Add(LogType.Identity, LogImpact.Low,
+ $"{ToPrettyString(player.Value):player} has changed the job icon of {ToPrettyString(uid):entity} to {jobIcon} ");
+ }
- if (!string.IsNullOrWhiteSpace(fullName))
- {
- fullName = fullName.Trim();
- if (fullName.Length > IdCardConsoleComponent.MaxFullNameLength)
- fullName = fullName[..IdCardConsoleComponent.MaxFullNameLength];
- }
- else
- {
- fullName = null;
- }
+ return true;
+ }
- if (id.FullName == fullName)
- return true;
- id.FullName = fullName;
- Dirty(id);
- UpdateEntityName(uid, id);
+ public bool TryChangeJobDepartment(EntityUid uid, JobPrototype job, IdCardComponent? id = null)
+ {
+ if (!Resolve(uid, ref id))
+ return false;
- if (player != null)
- {
- _adminLogger.Add(LogType.Identity, LogImpact.Low,
- $"{ToPrettyString(player.Value):player} has changed the name of {ToPrettyString(uid):entity} to {fullName} ");
- }
- return true;
+ foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
+ {
+ if (department.Roles.Contains(job.ID))
+ id.JobDepartments.Add("department-" + department.ID);
}
- /// <summary>
- /// Changes the name of the id's owner.
- /// </summary>
- /// <remarks>
- /// If either <see cref="FullName"/> or <see cref="JobTitle"/> is empty, it's replaced by placeholders.
- /// If both are empty, the original entity's name is restored.
- /// </remarks>
- private void UpdateEntityName(EntityUid uid, IdCardComponent? id = null)
+ Dirty(uid, id);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Attempts to change the full name of a card.
+ /// Returns true/false.
+ /// </summary>
+ /// <remarks>
+ /// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
+ /// </remarks>
+ public bool TryChangeFullName(EntityUid uid, string? fullName, IdCardComponent? id = null, EntityUid? player = null)
+ {
+ if (!Resolve(uid, ref id))
+ return false;
+
+ if (!string.IsNullOrWhiteSpace(fullName))
{
- if (!Resolve(uid, ref id))
- return;
+ fullName = fullName.Trim();
+ if (fullName.Length > IdCardConsoleComponent.MaxFullNameLength)
+ fullName = fullName[..IdCardConsoleComponent.MaxFullNameLength];
+ }
+ else
+ {
+ fullName = null;
+ }
- var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
+ if (id.FullName == fullName)
+ return true;
+ id.FullName = fullName;
+ Dirty(id);
+ UpdateEntityName(uid, id);
- var val = string.IsNullOrWhiteSpace(id.FullName)
- ? Loc.GetString("access-id-card-component-owner-name-job-title-text",
- ("jobSuffix", jobSuffix))
- : Loc.GetString("access-id-card-component-owner-full-name-job-title-text",
- ("fullName", id.FullName),
- ("jobSuffix", jobSuffix));
- _metaSystem.SetEntityName(uid, val);
+ if (player != null)
+ {
+ _adminLogger.Add(LogType.Identity, LogImpact.Low,
+ $"{ToPrettyString(player.Value):player} has changed the name of {ToPrettyString(uid):entity} to {fullName} ");
}
+ return true;
+ }
+
+ /// <summary>
+ /// Changes the name of the id's owner.
+ /// </summary>
+ /// <remarks>
+ /// If either <see cref="FullName"/> or <see cref="JobTitle"/> is empty, it's replaced by placeholders.
+ /// If both are empty, the original entity's name is restored.
+ /// </remarks>
+ private void UpdateEntityName(EntityUid uid, IdCardComponent? id = null)
+ {
+ if (!Resolve(uid, ref id))
+ return;
+
+ var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
+
+ var val = string.IsNullOrWhiteSpace(id.FullName)
+ ? Loc.GetString("access-id-card-component-owner-name-job-title-text",
+ ("jobSuffix", jobSuffix))
+ : Loc.GetString("access-id-card-component-owner-full-name-job-title-text",
+ ("fullName", id.FullName),
+ ("jobSuffix", jobSuffix));
+ _metaSystem.SetEntityName(uid, val);
}
}
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
-namespace Content.Server.Access.Systems
+namespace Content.Server.Access.Systems;
+
+public sealed class PresetIdCardSystem : EntitySystem
{
- public sealed class PresetIdCardSystem : EntitySystem
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IdCardSystem _cardSystem = default!;
+ [Dependency] private readonly SharedAccessSystem _accessSystem = default!;
+ [Dependency] private readonly StationSystem _stationSystem = default!;
+
+ public override void Initialize()
{
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IdCardSystem _cardSystem = default!;
- [Dependency] private readonly SharedAccessSystem _accessSystem = default!;
- [Dependency] private readonly StationSystem _stationSystem = default!;
+ SubscribeLocalEvent<PresetIdCardComponent, MapInitEvent>(OnMapInit);
- public override void Initialize()
- {
- SubscribeLocalEvent<PresetIdCardComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(PlayerJobsAssigned);
+ }
- SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(PlayerJobsAssigned);
- }
+ private void PlayerJobsAssigned(RulePlayerJobsAssignedEvent ev)
+ {
+ // Go over all ID cards and make sure they're correctly configured for extended access.
- private void PlayerJobsAssigned(RulePlayerJobsAssignedEvent ev)
+ var query = EntityQueryEnumerator<PresetIdCardComponent>();
+ while (query.MoveNext(out var uid, out var card))
{
- // Go over all ID cards and make sure they're correctly configured for extended access.
-
- var query = EntityQueryEnumerator<PresetIdCardComponent>();
- while (query.MoveNext(out var uid, out var card))
- {
- var station = _stationSystem.GetOwningStation(uid);
+ var station = _stationSystem.GetOwningStation(uid);
- // If we're not on an extended access station, the ID is already configured correctly from MapInit.
- if (station == null || !Comp<StationJobsComponent>(station.Value).ExtendedAccess)
- return;
+ // If we're not on an extended access station, the ID is already configured correctly from MapInit.
+ if (station == null || !Comp<StationJobsComponent>(station.Value).ExtendedAccess)
+ return;
- SetupIdAccess(uid, card, true);
- SetupIdName(uid, card);
- }
+ SetupIdAccess(uid, card, true);
+ SetupIdName(uid, card);
}
+ }
- private void OnMapInit(EntityUid uid, PresetIdCardComponent id, MapInitEvent args)
- {
- // If a preset ID card is spawned on a station at setup time,
- // the station may not exist,
- // or may not yet know whether it is on extended access (players not spawned yet).
- // PlayerJobsAssigned makes sure extended access is configured correctly in that case.
+ private void OnMapInit(EntityUid uid, PresetIdCardComponent id, MapInitEvent args)
+ {
+ // If a preset ID card is spawned on a station at setup time,
+ // the station may not exist,
+ // or may not yet know whether it is on extended access (players not spawned yet).
+ // PlayerJobsAssigned makes sure extended access is configured correctly in that case.
+
+ var station = _stationSystem.GetOwningStation(uid);
+ var extended = false;
+ if (station != null)
+ extended = Comp<StationJobsComponent>(station.Value).ExtendedAccess;
+
+ SetupIdAccess(uid, id, extended);
+ SetupIdName(uid, id);
+ }
- var station = _stationSystem.GetOwningStation(uid);
- var extended = false;
- if (station != null)
- extended = Comp<StationJobsComponent>(station.Value).ExtendedAccess;
+ private void SetupIdName(EntityUid uid, PresetIdCardComponent id)
+ {
+ if (id.IdName == null)
+ return;
+ _cardSystem.TryChangeFullName(uid, id.IdName);
+ }
- SetupIdAccess(uid, id, extended);
- SetupIdName(uid, id);
- }
+ private void SetupIdAccess(EntityUid uid, PresetIdCardComponent id, bool extended)
+ {
+ if (id.JobName == null)
+ return;
- private void SetupIdName(EntityUid uid, PresetIdCardComponent id)
+ if (!_prototypeManager.TryIndex(id.JobName, out JobPrototype? job))
{
- if (id.IdName == null)
- return;
- _cardSystem.TryChangeFullName(uid, id.IdName);
+ Log.Error($"Invalid job id ({id.JobName}) for preset card");
+ return;
}
- private void SetupIdAccess(EntityUid uid, PresetIdCardComponent id, bool extended)
- {
- if (id.JobName == null)
- return;
-
- if (!_prototypeManager.TryIndex(id.JobName, out JobPrototype? job))
- {
- Log.Error($"Invalid job id ({id.JobName}) for preset card");
- return;
- }
-
- _accessSystem.SetAccessToJob(uid, job, extended);
+ _accessSystem.SetAccessToJob(uid, job, extended);
- _cardSystem.TryChangeJobTitle(uid, job.LocalizedName);
+ _cardSystem.TryChangeJobTitle(uid, job.LocalizedName);
+ _cardSystem.TryChangeJobDepartment(uid, job);
- if (_prototypeManager.TryIndex<StatusIconPrototype>(job.Icon, out var jobIcon))
- {
- _cardSystem.TryChangeJobIcon(uid, jobIcon);
- }
+ if (_prototypeManager.TryIndex<StatusIconPrototype>(job.Icon, out var jobIcon))
+ {
+ _cardSystem.TryChangeJobIcon(uid, jobIcon);
}
}
}
using Content.Shared.Medical.SuitSensor;
-namespace Content.Server.Medical.CrewMonitoring
-{
- [RegisterComponent]
- [Access(typeof(CrewMonitoringConsoleSystem))]
- public sealed partial class CrewMonitoringConsoleComponent : Component
- {
- /// <summary>
- /// List of all currently connected sensors to this console.
- /// </summary>
- public Dictionary<string, SuitSensorStatus> ConnectedSensors = new();
-
- /// <summary>
- /// After what time sensor consider to be lost.
- /// </summary>
- [DataField("sensorTimeout"), ViewVariables(VVAccess.ReadWrite)]
- public float SensorTimeout = 10f;
+namespace Content.Server.Medical.CrewMonitoring;
- /// <summary>
- /// Whether the direction arrows in the monitor UI should snap the nearest diagonal or cardinal direction, or whether they should point exactly towards the target.
- /// </summary>
- [DataField("snap"), ViewVariables(VVAccess.ReadWrite)]
- public bool Snap = true;
+[RegisterComponent]
+[Access(typeof(CrewMonitoringConsoleSystem))]
+public sealed partial class CrewMonitoringConsoleComponent : Component
+{
+ /// <summary>
+ /// List of all currently connected sensors to this console.
+ /// </summary>
+ public Dictionary<string, SuitSensorStatus> ConnectedSensors = new();
- /// <summary>
- /// Minimum distance before the monitor direction indicator stops pointing towards the target and instead
- /// shows an icon indicating that the target is "here". Does not affect the displayed coordinates.
- /// </summary>
- [DataField("precision"), ViewVariables(VVAccess.ReadWrite)]
- public float Precision = 10f;
- }
+ /// <summary>
+ /// After what time sensor consider to be lost.
+ /// </summary>
+ [DataField("sensorTimeout"), ViewVariables(VVAccess.ReadWrite)]
+ public float SensorTimeout = 10f;
}
using Content.Server.PowerCell;
using Content.Shared.Medical.CrewMonitoring;
using Content.Shared.Medical.SuitSensor;
+using Content.Shared.Pinpointer;
using Robust.Server.GameObjects;
-namespace Content.Server.Medical.CrewMonitoring
+namespace Content.Server.Medical.CrewMonitoring;
+
+public sealed class CrewMonitoringConsoleSystem : EntitySystem
{
- public sealed class CrewMonitoringConsoleSystem : EntitySystem
+ [Dependency] private readonly PowerCellSystem _cell = default!;
+ [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<CrewMonitoringConsoleComponent, ComponentRemove>(OnRemove);
+ SubscribeLocalEvent<CrewMonitoringConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+ SubscribeLocalEvent<CrewMonitoringConsoleComponent, BoundUIOpenedEvent>(OnUIOpened);
+ }
+
+ private void OnRemove(EntityUid uid, CrewMonitoringConsoleComponent component, ComponentRemove args)
+ {
+ component.ConnectedSensors.Clear();
+ }
+
+ private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, DeviceNetworkPacketEvent args)
{
- [Dependency] private readonly PowerCellSystem _cell = default!;
- [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<CrewMonitoringConsoleComponent, ComponentRemove>(OnRemove);
- SubscribeLocalEvent<CrewMonitoringConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
- SubscribeLocalEvent<CrewMonitoringConsoleComponent, BoundUIOpenedEvent>(OnUIOpened);
- }
-
- private void OnRemove(EntityUid uid, CrewMonitoringConsoleComponent component, ComponentRemove args)
- {
- component.ConnectedSensors.Clear();
- }
-
- private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, DeviceNetworkPacketEvent args)
- {
- var payload = args.Data;
- // check command
- if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
- return;
- if (command != DeviceNetworkConstants.CmdUpdatedState)
- return;
- if (!payload.TryGetValue(SuitSensorConstants.NET_STATUS_COLLECTION, out Dictionary<string, SuitSensorStatus>? sensorStatus))
- return;
-
- component.ConnectedSensors = sensorStatus;
- UpdateUserInterface(uid, component);
- }
-
- private void OnUIOpened(EntityUid uid, CrewMonitoringConsoleComponent component, BoundUIOpenedEvent args)
- {
- if (!_cell.TryUseActivatableCharge(uid))
- return;
-
- UpdateUserInterface(uid, component);
- }
-
- private void UpdateUserInterface(EntityUid uid, CrewMonitoringConsoleComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
-
- if (!_uiSystem.TryGetUi(uid, CrewMonitoringUIKey.Key, out var bui))
- return;
-
- // update all sensors info
- var allSensors = component.ConnectedSensors.Values.ToList();
- _uiSystem.SetUiState(bui, new CrewMonitoringState(allSensors, component.Snap, component.Precision));
- }
+ var payload = args.Data;
+
+ // Check command
+ if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
+ return;
+
+ if (command != DeviceNetworkConstants.CmdUpdatedState)
+ return;
+
+ if (!payload.TryGetValue(SuitSensorConstants.NET_STATUS_COLLECTION, out Dictionary<string, SuitSensorStatus>? sensorStatus))
+ return;
+
+ component.ConnectedSensors = sensorStatus;
+ UpdateUserInterface(uid, component);
+ }
+
+ private void OnUIOpened(EntityUid uid, CrewMonitoringConsoleComponent component, BoundUIOpenedEvent args)
+ {
+ if (!_cell.TryUseActivatableCharge(uid))
+ return;
+
+ UpdateUserInterface(uid, component);
+ }
+
+ private void UpdateUserInterface(EntityUid uid, CrewMonitoringConsoleComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+
+ if (!_uiSystem.TryGetUi(uid, CrewMonitoringUIKey.Key, out var bui))
+ return;
+
+ // The grid must have a NavMapComponent to visualize the map in the UI
+ var xform = Transform(uid);
+
+ if (xform.GridUid != null)
+ EnsureComp<NavMapComponent>(xform.GridUid.Value);
+
+ // Update all sensors info
+ var allSensors = component.ConnectedSensors.Values.ToList();
+ _uiSystem.SetUiState(bui, new CrewMonitoringState(allSensors));
}
}
using Content.Shared.Medical.SuitSensor;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-namespace Content.Server.Medical.SuitSensors
+namespace Content.Server.Medical.SuitSensors;
+
+/// <summary>
+/// Tracking device, embedded in almost all uniforms and jumpsuits.
+/// If enabled, will report to crew monitoring console owners position and status.
+/// </summary>
+[RegisterComponent]
+[Access(typeof(SuitSensorSystem))]
+public sealed partial class SuitSensorComponent : Component
{
/// <summary>
- /// Tracking device, embedded in almost all uniforms and jumpsuits.
- /// If enabled, will report to crew monitoring console owners position and status.
+ /// Choose a random sensor mode when item is spawned.
/// </summary>
- [RegisterComponent]
- [Access(typeof(SuitSensorSystem))]
- public sealed partial class SuitSensorComponent : Component
- {
- /// <summary>
- /// Choose a random sensor mode when item is spawned.
- /// </summary>
- [DataField("randomMode")]
- public bool RandomMode = true;
+ [DataField("randomMode")]
+ public bool RandomMode = true;
- /// <summary>
- /// If true user can't change suit sensor mode
- /// </summary>
- [DataField("controlsLocked")]
- public bool ControlsLocked = false;
+ /// <summary>
+ /// If true user can't change suit sensor mode
+ /// </summary>
+ [DataField("controlsLocked")]
+ public bool ControlsLocked = false;
- /// <summary>
- /// Current sensor mode. Can be switched by user verbs.
- /// </summary>
- [DataField("mode")]
- public SuitSensorMode Mode = SuitSensorMode.SensorOff;
+ /// <summary>
+ /// Current sensor mode. Can be switched by user verbs.
+ /// </summary>
+ [DataField("mode")]
+ public SuitSensorMode Mode = SuitSensorMode.SensorOff;
- /// <summary>
- /// Activate sensor if user wear it in this slot.
- /// </summary>
- [DataField("activationSlot")]
- public string ActivationSlot = "jumpsuit";
+ /// <summary>
+ /// Activate sensor if user wear it in this slot.
+ /// </summary>
+ [DataField("activationSlot")]
+ public string ActivationSlot = "jumpsuit";
- /// <summary>
- /// Activate sensor if user has this in a sensor-compatible container.
- /// </summary>
- [DataField("activationContainer")]
- public string? ActivationContainer;
+ /// <summary>
+ /// Activate sensor if user has this in a sensor-compatible container.
+ /// </summary>
+ [DataField("activationContainer")]
+ public string? ActivationContainer;
- /// <summary>
- /// How often does sensor update its owners status (in seconds). Limited by the system update rate.
- /// </summary>
- [DataField("updateRate")]
- public TimeSpan UpdateRate = TimeSpan.FromSeconds(2f);
+ /// <summary>
+ /// How often does sensor update its owners status (in seconds). Limited by the system update rate.
+ /// </summary>
+ [DataField("updateRate")]
+ public TimeSpan UpdateRate = TimeSpan.FromSeconds(2f);
- /// <summary>
- /// Current user that wears suit sensor. Null if nobody wearing it.
- /// </summary>
- [ViewVariables]
- public EntityUid? User = null;
+ /// <summary>
+ /// Current user that wears suit sensor. Null if nobody wearing it.
+ /// </summary>
+ [ViewVariables]
+ public EntityUid? User = null;
- /// <summary>
- /// Next time when sensor updated owners status
- /// </summary>
- [DataField("nextUpdate", customTypeSerializer:typeof(TimeOffsetSerializer))]
- public TimeSpan NextUpdate = TimeSpan.Zero;
+ /// <summary>
+ /// Next time when sensor updated owners status
+ /// </summary>
+ [DataField("nextUpdate", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate = TimeSpan.Zero;
- /// <summary>
- /// The station this suit sensor belongs to. If it's null the suit didn't spawn on a station and the sensor doesn't work.
- /// </summary>
- [DataField("station")]
- public EntityUid? StationId = null;
+ /// <summary>
+ /// The station this suit sensor belongs to. If it's null the suit didn't spawn on a station and the sensor doesn't work.
+ /// </summary>
+ [DataField("station")]
+ public EntityUid? StationId = null;
- /// <summary>
- /// The server the suit sensor sends it state to.
- /// The suit sensor will try connecting to a new server when no server is connected.
- /// It does this by calling the servers entity system for performance reasons.
- /// </summary>
- [DataField("server")]
- public string? ConnectedServer = null;
- }
+ /// <summary>
+ /// The server the suit sensor sends it state to.
+ /// The suit sensor will try connecting to a new server when no server is connected.
+ /// It does this by calling the servers entity system for performance reasons.
+ /// </summary>
+ [DataField("server")]
+ public string? ConnectedServer = null;
}
using Robust.Shared.Random;
using Robust.Shared.Timing;
-namespace Content.Server.Medical.SuitSensors
+namespace Content.Server.Medical.SuitSensors;
+
+public sealed class SuitSensorSystem : EntitySystem
{
- public sealed class SuitSensorSystem : EntitySystem
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly CrewMonitoringServerSystem _monitoringServerSystem = default!;
+ [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
+ [Dependency] private readonly IdCardSystem _idCardSystem = default!;
+ [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
+ [Dependency] private readonly PopupSystem _popupSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly StationSystem _stationSystem = default!;
+
+ public override void Initialize()
{
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly CrewMonitoringServerSystem _monitoringServerSystem = default!;
- [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
- [Dependency] private readonly IdCardSystem _idCardSystem = default!;
- [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly StationSystem _stationSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
- SubscribeLocalEvent<SuitSensorComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<SuitSensorComponent, EntityUnpausedEvent>(OnUnpaused);
- SubscribeLocalEvent<SuitSensorComponent, GotEquippedEvent>(OnEquipped);
- SubscribeLocalEvent<SuitSensorComponent, GotUnequippedEvent>(OnUnequipped);
- SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
- SubscribeLocalEvent<SuitSensorComponent, GetVerbsEvent<Verb>>(OnVerb);
- SubscribeLocalEvent<SuitSensorComponent, EntGotInsertedIntoContainerMessage>(OnInsert);
- SubscribeLocalEvent<SuitSensorComponent, EntGotRemovedFromContainerMessage>(OnRemove);
- }
-
- private void OnUnpaused(EntityUid uid, SuitSensorComponent component, ref EntityUnpausedEvent args)
- {
- component.NextUpdate += args.PausedTime;
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- var curTime = _gameTiming.CurTime;
- var sensors = EntityManager.EntityQueryEnumerator<SuitSensorComponent, DeviceNetworkComponent>();
+ base.Initialize();
+ SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
+ SubscribeLocalEvent<SuitSensorComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<SuitSensorComponent, EntityUnpausedEvent>(OnUnpaused);
+ SubscribeLocalEvent<SuitSensorComponent, GotEquippedEvent>(OnEquipped);
+ SubscribeLocalEvent<SuitSensorComponent, GotUnequippedEvent>(OnUnequipped);
+ SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
+ SubscribeLocalEvent<SuitSensorComponent, GetVerbsEvent<Verb>>(OnVerb);
+ SubscribeLocalEvent<SuitSensorComponent, EntGotInsertedIntoContainerMessage>(OnInsert);
+ SubscribeLocalEvent<SuitSensorComponent, EntGotRemovedFromContainerMessage>(OnRemove);
+ }
- while (sensors.MoveNext(out var uid, out var sensor, out var device))
- {
- if (device.TransmitFrequency is null)
- continue;
+ private void OnUnpaused(EntityUid uid, SuitSensorComponent component, ref EntityUnpausedEvent args)
+ {
+ component.NextUpdate += args.PausedTime;
+ }
- // check if sensor is ready to update
- if (curTime < sensor.NextUpdate)
- continue;
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
- if (!CheckSensorAssignedStation(uid, sensor))
- continue;
+ var curTime = _gameTiming.CurTime;
+ var sensors = EntityManager.EntityQueryEnumerator<SuitSensorComponent, DeviceNetworkComponent>();
- // TODO: This would cause imprecision at different tick rates.
- sensor.NextUpdate = curTime + sensor.UpdateRate;
+ while (sensors.MoveNext(out var uid, out var sensor, out var device))
+ {
+ if (device.TransmitFrequency is null)
+ continue;
- // get sensor status
- var status = GetSensorState(uid, sensor);
- if (status == null)
- continue;
+ // check if sensor is ready to update
+ if (curTime < sensor.NextUpdate)
+ continue;
- //Retrieve active server address if the sensor isn't connected to a server
- if (sensor.ConnectedServer == null)
- {
- if (!_monitoringServerSystem.TryGetActiveServerAddress(sensor.StationId!.Value, out var address))
- continue;
+ if (!CheckSensorAssignedStation(uid, sensor))
+ continue;
- sensor.ConnectedServer = address;
- }
+ // TODO: This would cause imprecision at different tick rates.
+ sensor.NextUpdate = curTime + sensor.UpdateRate;
- // Send it to the connected server
- var payload = SuitSensorToPacket(status);
+ // get sensor status
+ var status = GetSensorState(uid, sensor);
+ if (status == null)
+ continue;
- // Clear the connected server if its address isn't on the network
- if (!_deviceNetworkSystem.IsAddressPresent(device.DeviceNetId, sensor.ConnectedServer))
- {
- sensor.ConnectedServer = null;
+ //Retrieve active server address if the sensor isn't connected to a server
+ if (sensor.ConnectedServer == null)
+ {
+ if (!_monitoringServerSystem.TryGetActiveServerAddress(sensor.StationId!.Value, out var address))
continue;
- }
- _deviceNetworkSystem.QueuePacket(uid, sensor.ConnectedServer, payload, device: device);
+ sensor.ConnectedServer = address;
}
- }
- /// <summary>
- /// Checks whether the sensor is assigned to a station or not
- /// and tries to assign an unassigned sensor to a station if it's currently on a grid
- /// </summary>
- /// <returns>True if the sensor is assigned to a station or assigning it was successful. False otherwise.</returns>
- private bool CheckSensorAssignedStation(EntityUid uid, SuitSensorComponent sensor)
- {
- if (!sensor.StationId.HasValue && Transform(uid).GridUid == null)
- return false;
+ // Send it to the connected server
+ var payload = SuitSensorToPacket(status);
- sensor.StationId = _stationSystem.GetOwningStation(uid);
- return sensor.StationId.HasValue;
- }
+ // Clear the connected server if its address isn't on the network
+ if (!_deviceNetworkSystem.IsAddressPresent(device.DeviceNetId, sensor.ConnectedServer))
+ {
+ sensor.ConnectedServer = null;
+ continue;
+ }
- private void OnPlayerSpawn(PlayerSpawnCompleteEvent ev)
- {
- // If the player spawns in arrivals then the grid underneath them may not be appropriate.
- // in which case we'll just use the station spawn code told us they are attached to and set all of their
- // sensors.
- var sensorQuery = GetEntityQuery<SuitSensorComponent>();
- var xformQuery = GetEntityQuery<TransformComponent>();
- RecursiveSensor(ev.Mob, ev.Station, sensorQuery, xformQuery);
+ _deviceNetworkSystem.QueuePacket(uid, sensor.ConnectedServer, payload, device: device);
}
+ }
- private void RecursiveSensor(EntityUid uid, EntityUid stationUid, EntityQuery<SuitSensorComponent> sensorQuery, EntityQuery<TransformComponent> xformQuery)
- {
- var xform = xformQuery.GetComponent(uid);
- var enumerator = xform.ChildEnumerator;
+ /// <summary>
+ /// Checks whether the sensor is assigned to a station or not
+ /// and tries to assign an unassigned sensor to a station if it's currently on a grid
+ /// </summary>
+ /// <returns>True if the sensor is assigned to a station or assigning it was successful. False otherwise.</returns>
+ private bool CheckSensorAssignedStation(EntityUid uid, SuitSensorComponent sensor)
+ {
+ if (!sensor.StationId.HasValue && Transform(uid).GridUid == null)
+ return false;
- while (enumerator.MoveNext(out var child))
- {
- if (sensorQuery.TryGetComponent(child, out var sensor))
- {
- sensor.StationId = stationUid;
- }
+ sensor.StationId = _stationSystem.GetOwningStation(uid);
+ return sensor.StationId.HasValue;
+ }
- RecursiveSensor(child.Value, stationUid, sensorQuery, xformQuery);
- }
- }
+ private void OnPlayerSpawn(PlayerSpawnCompleteEvent ev)
+ {
+ // If the player spawns in arrivals then the grid underneath them may not be appropriate.
+ // in which case we'll just use the station spawn code told us they are attached to and set all of their
+ // sensors.
+ var sensorQuery = GetEntityQuery<SuitSensorComponent>();
+ var xformQuery = GetEntityQuery<TransformComponent>();
+ RecursiveSensor(ev.Mob, ev.Station, sensorQuery, xformQuery);
+ }
- private void OnMapInit(EntityUid uid, SuitSensorComponent component, MapInitEvent args)
- {
- // Fallback
- component.StationId ??= _stationSystem.GetOwningStation(uid);
+ private void RecursiveSensor(EntityUid uid, EntityUid stationUid, EntityQuery<SuitSensorComponent> sensorQuery, EntityQuery<TransformComponent> xformQuery)
+ {
+ var xform = xformQuery.GetComponent(uid);
+ var enumerator = xform.ChildEnumerator;
- // generate random mode
- if (component.RandomMode)
+ while (enumerator.MoveNext(out var child))
+ {
+ if (sensorQuery.TryGetComponent(child, out var sensor))
{
- //make the sensor mode favor higher levels, except coords.
- var modesDist = new[]
- {
- SuitSensorMode.SensorOff,
- SuitSensorMode.SensorBinary, SuitSensorMode.SensorBinary,
- SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals,
- SuitSensorMode.SensorCords, SuitSensorMode.SensorCords
- };
- component.Mode = _random.Pick(modesDist);
+ sensor.StationId = stationUid;
}
+
+ RecursiveSensor(child.Value, stationUid, sensorQuery, xformQuery);
}
+ }
- private void OnEquipped(EntityUid uid, SuitSensorComponent component, GotEquippedEvent args)
- {
- if (args.Slot != component.ActivationSlot)
- return;
+ private void OnMapInit(EntityUid uid, SuitSensorComponent component, MapInitEvent args)
+ {
+ // Fallback
+ component.StationId ??= _stationSystem.GetOwningStation(uid);
- component.User = args.Equipee;
+ // generate random mode
+ if (component.RandomMode)
+ {
+ //make the sensor mode favor higher levels, except coords.
+ var modesDist = new[]
+ {
+ SuitSensorMode.SensorOff,
+ SuitSensorMode.SensorBinary, SuitSensorMode.SensorBinary,
+ SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals,
+ SuitSensorMode.SensorCords, SuitSensorMode.SensorCords
+ };
+ component.Mode = _random.Pick(modesDist);
}
+ }
- private void OnUnequipped(EntityUid uid, SuitSensorComponent component, GotUnequippedEvent args)
- {
- if (args.Slot != component.ActivationSlot)
- return;
+ private void OnEquipped(EntityUid uid, SuitSensorComponent component, GotEquippedEvent args)
+ {
+ if (args.Slot != component.ActivationSlot)
+ return;
- component.User = null;
- }
+ component.User = args.Equipee;
+ }
- private void OnExamine(EntityUid uid, SuitSensorComponent component, ExaminedEvent args)
- {
- if (!args.IsInDetailsRange)
- return;
+ private void OnUnequipped(EntityUid uid, SuitSensorComponent component, GotUnequippedEvent args)
+ {
+ if (args.Slot != component.ActivationSlot)
+ return;
- string msg;
- switch (component.Mode)
- {
- case SuitSensorMode.SensorOff:
- msg = "suit-sensor-examine-off";
- break;
- case SuitSensorMode.SensorBinary:
- msg = "suit-sensor-examine-binary";
- break;
- case SuitSensorMode.SensorVitals:
- msg = "suit-sensor-examine-vitals";
- break;
- case SuitSensorMode.SensorCords:
- msg = "suit-sensor-examine-cords";
- break;
- default:
- return;
- }
+ component.User = null;
+ }
- args.PushMarkup(Loc.GetString(msg));
- }
+ private void OnExamine(EntityUid uid, SuitSensorComponent component, ExaminedEvent args)
+ {
+ if (!args.IsInDetailsRange)
+ return;
- private void OnVerb(EntityUid uid, SuitSensorComponent component, GetVerbsEvent<Verb> args)
+ string msg;
+ switch (component.Mode)
{
- // check if user can change sensor
- if (component.ControlsLocked)
+ case SuitSensorMode.SensorOff:
+ msg = "suit-sensor-examine-off";
+ break;
+ case SuitSensorMode.SensorBinary:
+ msg = "suit-sensor-examine-binary";
+ break;
+ case SuitSensorMode.SensorVitals:
+ msg = "suit-sensor-examine-vitals";
+ break;
+ case SuitSensorMode.SensorCords:
+ msg = "suit-sensor-examine-cords";
+ break;
+ default:
return;
+ }
- // standard interaction checks
- if (!args.CanAccess || !args.CanInteract || args.Hands == null)
- return;
+ args.PushMarkup(Loc.GetString(msg));
+ }
- args.Verbs.UnionWith(new[]
- {
- CreateVerb(uid, component, args.User, SuitSensorMode.SensorOff),
- CreateVerb(uid, component, args.User, SuitSensorMode.SensorBinary),
- CreateVerb(uid, component, args.User, SuitSensorMode.SensorVitals),
- CreateVerb(uid, component, args.User, SuitSensorMode.SensorCords)
- });
- }
+ private void OnVerb(EntityUid uid, SuitSensorComponent component, GetVerbsEvent<Verb> args)
+ {
+ // check if user can change sensor
+ if (component.ControlsLocked)
+ return;
- private void OnInsert(EntityUid uid, SuitSensorComponent component, EntGotInsertedIntoContainerMessage args)
+ // standard interaction checks
+ if (!args.CanAccess || !args.CanInteract || args.Hands == null)
+ return;
+
+ args.Verbs.UnionWith(new[]
{
- if (args.Container.ID != component.ActivationContainer)
- return;
+ CreateVerb(uid, component, args.User, SuitSensorMode.SensorOff),
+ CreateVerb(uid, component, args.User, SuitSensorMode.SensorBinary),
+ CreateVerb(uid, component, args.User, SuitSensorMode.SensorVitals),
+ CreateVerb(uid, component, args.User, SuitSensorMode.SensorCords)
+ });
+ }
- component.User = args.Container.Owner;
- }
+ private void OnInsert(EntityUid uid, SuitSensorComponent component, EntGotInsertedIntoContainerMessage args)
+ {
+ if (args.Container.ID != component.ActivationContainer)
+ return;
- private void OnRemove(EntityUid uid, SuitSensorComponent component, EntGotRemovedFromContainerMessage args)
- {
- if (args.Container.ID != component.ActivationContainer)
- return;
+ component.User = args.Container.Owner;
+ }
- component.User = null;
- }
+ private void OnRemove(EntityUid uid, SuitSensorComponent component, EntGotRemovedFromContainerMessage args)
+ {
+ if (args.Container.ID != component.ActivationContainer)
+ return;
- private Verb CreateVerb(EntityUid uid, SuitSensorComponent component, EntityUid userUid, SuitSensorMode mode)
- {
- return new Verb()
- {
- Text = GetModeName(mode),
- Disabled = component.Mode == mode,
- Priority = -(int) mode, // sort them in descending order
- Category = VerbCategory.SetSensor,
- Act = () => SetSensor(uid, mode, userUid, component)
- };
- }
+ component.User = null;
+ }
- private string GetModeName(SuitSensorMode mode)
+ private Verb CreateVerb(EntityUid uid, SuitSensorComponent component, EntityUid userUid, SuitSensorMode mode)
+ {
+ return new Verb()
{
- string name;
- switch (mode)
- {
- case SuitSensorMode.SensorOff:
- name = "suit-sensor-mode-off";
- break;
- case SuitSensorMode.SensorBinary:
- name = "suit-sensor-mode-binary";
- break;
- case SuitSensorMode.SensorVitals:
- name = "suit-sensor-mode-vitals";
- break;
- case SuitSensorMode.SensorCords:
- name = "suit-sensor-mode-cords";
- break;
- default:
- return "";
- }
+ Text = GetModeName(mode),
+ Disabled = component.Mode == mode,
+ Priority = -(int) mode, // sort them in descending order
+ Category = VerbCategory.SetSensor,
+ Act = () => SetSensor(uid, mode, userUid, component)
+ };
+ }
- return Loc.GetString(name);
+ private string GetModeName(SuitSensorMode mode)
+ {
+ string name;
+ switch (mode)
+ {
+ case SuitSensorMode.SensorOff:
+ name = "suit-sensor-mode-off";
+ break;
+ case SuitSensorMode.SensorBinary:
+ name = "suit-sensor-mode-binary";
+ break;
+ case SuitSensorMode.SensorVitals:
+ name = "suit-sensor-mode-vitals";
+ break;
+ case SuitSensorMode.SensorCords:
+ name = "suit-sensor-mode-cords";
+ break;
+ default:
+ return "";
}
- public void SetSensor(EntityUid uid, SuitSensorMode mode, EntityUid? userUid = null,
- SuitSensorComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
+ return Loc.GetString(name);
+ }
- component.Mode = mode;
+ public void SetSensor(EntityUid uid, SuitSensorMode mode, EntityUid? userUid = null,
+ SuitSensorComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
- if (userUid != null)
- {
- var msg = Loc.GetString("suit-sensor-mode-state", ("mode", GetModeName(mode)));
- _popupSystem.PopupEntity(msg, uid, userUid.Value);
- }
- }
+ component.Mode = mode;
- public SuitSensorStatus? GetSensorState(EntityUid uid, SuitSensorComponent? sensor = null, TransformComponent? transform = null)
+ if (userUid != null)
{
- if (!Resolve(uid, ref sensor, ref transform))
- return null;
+ var msg = Loc.GetString("suit-sensor-mode-state", ("mode", GetModeName(mode)));
+ _popupSystem.PopupEntity(msg, uid, userUid.Value);
+ }
+ }
- // check if sensor is enabled and worn by user
- if (sensor.Mode == SuitSensorMode.SensorOff || sensor.User == null || transform.GridUid == null)
- return null;
+ public SuitSensorStatus? GetSensorState(EntityUid uid, SuitSensorComponent? sensor = null, TransformComponent? transform = null)
+ {
+ if (!Resolve(uid, ref sensor, ref transform))
+ return null;
- // try to get mobs id from ID slot
- var userName = Loc.GetString("suit-sensor-component-unknown-name");
- var userJob = Loc.GetString("suit-sensor-component-unknown-job");
- if (_idCardSystem.TryFindIdCard(sensor.User.Value, out var card))
- {
- if (card.Comp.FullName != null)
- userName = card.Comp.FullName;
- if (card.Comp.JobTitle != null)
- userJob = card.Comp.JobTitle;
- }
+ // check if sensor is enabled and worn by user
+ if (sensor.Mode == SuitSensorMode.SensorOff || sensor.User == null || transform.GridUid == null)
+ return null;
- // get health mob state
- var isAlive = false;
- if (EntityManager.TryGetComponent(sensor.User.Value, out MobStateComponent? mobState))
- isAlive = !_mobStateSystem.IsDead(sensor.User.Value, mobState);
+ // try to get mobs id from ID slot
+ var userName = Loc.GetString("suit-sensor-component-unknown-name");
+ var userJob = Loc.GetString("suit-sensor-component-unknown-job");
+ var userJobIcon = "JobIconNoId";
+ var userJobDepartments = new List<string>();
- // get mob total damage
- var totalDamage = 0;
- if (TryComp<DamageableComponent>(sensor.User.Value, out var damageable))
- totalDamage = damageable.TotalDamage.Int();
+ if (_idCardSystem.TryFindIdCard(sensor.User.Value, out var card))
+ {
+ if (card.Comp.FullName != null)
+ userName = card.Comp.FullName;
+ if (card.Comp.JobTitle != null)
+ userJob = card.Comp.JobTitle;
+ if (card.Comp.JobIcon != null)
+ userJobIcon = card.Comp.JobIcon;
+
+ foreach (var department in card.Comp.JobDepartments)
+ userJobDepartments.Add(Loc.GetString(department));
+ }
- // finally, form suit sensor status
- var status = new SuitSensorStatus(GetNetEntity(uid), userName, userJob);
- switch (sensor.Mode)
- {
- case SuitSensorMode.SensorBinary:
- status.IsAlive = isAlive;
- break;
- case SuitSensorMode.SensorVitals:
- status.IsAlive = isAlive;
- status.TotalDamage = totalDamage;
- break;
- case SuitSensorMode.SensorCords:
- status.IsAlive = isAlive;
- status.TotalDamage = totalDamage;
- EntityCoordinates coordinates;
- var xformQuery = GetEntityQuery<TransformComponent>();
-
- if (transform.GridUid != null)
- {
- coordinates = new EntityCoordinates(transform.GridUid.Value,
- _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery)
- .Transform(_transform.GetWorldPosition(transform, xformQuery)));
- }
- else if (transform.MapUid != null)
- {
- coordinates = new EntityCoordinates(transform.MapUid.Value,
- _transform.GetWorldPosition(transform, xformQuery));
- }
- else
- {
- coordinates = EntityCoordinates.Invalid;
- }
-
- status.Coordinates = GetNetCoordinates(coordinates);
- break;
- }
+ // get health mob state
+ var isAlive = false;
+ if (EntityManager.TryGetComponent(sensor.User.Value, out MobStateComponent? mobState))
+ isAlive = !_mobStateSystem.IsDead(sensor.User.Value, mobState);
- return status;
- }
+ // get mob total damage
+ var totalDamage = 0;
+ if (TryComp<DamageableComponent>(sensor.User.Value, out var damageable))
+ totalDamage = damageable.TotalDamage.Int();
- /// <summary>
- /// Serialize create a device network package from the suit sensors status.
- /// </summary>
- public NetworkPayload SuitSensorToPacket(SuitSensorStatus status)
+ // finally, form suit sensor status
+ var status = new SuitSensorStatus(GetNetEntity(uid), userName, userJob, userJobIcon, userJobDepartments);
+ switch (sensor.Mode)
{
- var payload = new NetworkPayload()
- {
- [DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdUpdatedState,
- [SuitSensorConstants.NET_NAME] = status.Name,
- [SuitSensorConstants.NET_JOB] = status.Job,
- [SuitSensorConstants.NET_IS_ALIVE] = status.IsAlive,
- [SuitSensorConstants.NET_SUIT_SENSOR_UID] = status.SuitSensorUid,
- };
+ case SuitSensorMode.SensorBinary:
+ status.IsAlive = isAlive;
+ break;
+ case SuitSensorMode.SensorVitals:
+ status.IsAlive = isAlive;
+ status.TotalDamage = totalDamage;
+ break;
+ case SuitSensorMode.SensorCords:
+ status.IsAlive = isAlive;
+ status.TotalDamage = totalDamage;
+ EntityCoordinates coordinates;
+ var xformQuery = GetEntityQuery<TransformComponent>();
+
+ if (transform.GridUid != null)
+ {
+ coordinates = new EntityCoordinates(transform.GridUid.Value,
+ _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery)
+ .Transform(_transform.GetWorldPosition(transform, xformQuery)));
+ }
+ else if (transform.MapUid != null)
+ {
+ coordinates = new EntityCoordinates(transform.MapUid.Value,
+ _transform.GetWorldPosition(transform, xformQuery));
+ }
+ else
+ {
+ coordinates = EntityCoordinates.Invalid;
+ }
- if (status.TotalDamage != null)
- payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE, status.TotalDamage);
- if (status.Coordinates != null)
- payload.Add(SuitSensorConstants.NET_COORDINATES, status.Coordinates);
+ status.Coordinates = GetNetCoordinates(coordinates);
+ break;
+ }
+ return status;
+ }
- return payload;
- }
+ /// <summary>
+ /// Serialize create a device network package from the suit sensors status.
+ /// </summary>
+ public NetworkPayload SuitSensorToPacket(SuitSensorStatus status)
+ {
+ var payload = new NetworkPayload()
+ {
+ [DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdUpdatedState,
+ [SuitSensorConstants.NET_NAME] = status.Name,
+ [SuitSensorConstants.NET_JOB] = status.Job,
+ [SuitSensorConstants.NET_JOB_ICON] = status.JobIcon,
+ [SuitSensorConstants.NET_JOB_DEPARTMENTS] = status.JobDepartments,
+ [SuitSensorConstants.NET_IS_ALIVE] = status.IsAlive,
+ [SuitSensorConstants.NET_SUIT_SENSOR_UID] = status.SuitSensorUid,
+ };
+
+ if (status.TotalDamage != null)
+ payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE, status.TotalDamage);
+ if (status.Coordinates != null)
+ payload.Add(SuitSensorConstants.NET_COORDINATES, status.Coordinates);
+
+ return payload;
+ }
- /// <summary>
- /// Try to create the suit sensors status from the device network message
- /// </summary>
- public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload)
+ /// <summary>
+ /// Try to create the suit sensors status from the device network message
+ /// </summary>
+ public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload)
+ {
+ // check command
+ if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
+ return null;
+ if (command != DeviceNetworkConstants.CmdUpdatedState)
+ return null;
+
+ // check name, job and alive
+ if (!payload.TryGetValue(SuitSensorConstants.NET_NAME, out string? name)) return null;
+ if (!payload.TryGetValue(SuitSensorConstants.NET_JOB, out string? job)) return null;
+ if (!payload.TryGetValue(SuitSensorConstants.NET_JOB_ICON, out string? jobIcon)) return null;
+ if (!payload.TryGetValue(SuitSensorConstants.NET_JOB_DEPARTMENTS, out List<string>? jobDepartments)) return null;
+ if (!payload.TryGetValue(SuitSensorConstants.NET_IS_ALIVE, out bool? isAlive)) return null;
+ if (!payload.TryGetValue(SuitSensorConstants.NET_SUIT_SENSOR_UID, out NetEntity suitSensorUid)) return null;
+
+ // try get total damage and cords (optionals)
+ payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE, out int? totalDamage);
+ payload.TryGetValue(SuitSensorConstants.NET_COORDINATES, out NetCoordinates? coords);
+
+ var status = new SuitSensorStatus(suitSensorUid, name, job, jobIcon, jobDepartments)
{
- // check command
- if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
- return null;
- if (command != DeviceNetworkConstants.CmdUpdatedState)
- return null;
-
- // check name, job and alive
- if (!payload.TryGetValue(SuitSensorConstants.NET_NAME, out string? name)) return null;
- if (!payload.TryGetValue(SuitSensorConstants.NET_JOB, out string? job)) return null;
- if (!payload.TryGetValue(SuitSensorConstants.NET_IS_ALIVE, out bool? isAlive)) return null;
- if (!payload.TryGetValue(SuitSensorConstants.NET_SUIT_SENSOR_UID, out NetEntity suitSensorUid)) return null;
-
- // try get total damage and cords (optionals)
- payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE, out int? totalDamage);
- payload.TryGetValue(SuitSensorConstants.NET_COORDINATES, out NetCoordinates? coords);
-
- var status = new SuitSensorStatus(suitSensorUid, name, job)
- {
- IsAlive = isAlive.Value,
- TotalDamage = totalDamage,
- Coordinates = coords,
- };
- return status;
- }
+ IsAlive = isAlive.Value,
+ TotalDamage = totalDamage,
+ Coordinates = coords,
+ };
+ return status;
}
}
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Shared.Access.Components
+namespace Content.Shared.Access.Components;
+
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
+[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
+public sealed partial class IdCardComponent : Component
{
- [RegisterComponent, NetworkedComponent]
- [AutoGenerateComponentState]
- [Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
- public sealed partial class IdCardComponent : Component
- {
- [DataField("fullName"), ViewVariables(VVAccess.ReadWrite)]
- [AutoNetworkedField]
- // FIXME Friends
- public string? FullName;
+ [DataField("fullName"), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ // FIXME Friends
+ public string? FullName;
- [DataField("jobTitle")]
- [AutoNetworkedField]
- [Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite), ViewVariables(VVAccess.ReadWrite)]
- public string? JobTitle;
+ [DataField("jobTitle")]
+ [AutoNetworkedField]
+ [Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite), ViewVariables(VVAccess.ReadWrite)]
+ public string? JobTitle;
- /// <summary>
- /// The state of the job icon rsi.
- /// </summary>
- [DataField("jobIcon", customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))]
- [AutoNetworkedField]
- public string JobIcon = "JobIconUnknown";
+ /// <summary>
+ /// The state of the job icon rsi.
+ /// </summary>
+ [DataField("jobIcon", customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))]
+ [AutoNetworkedField]
+ public string JobIcon = "JobIconUnknown";
- }
+ /// <summary>
+ /// The unlocalized names of the departments associated with the job
+ /// </summary>
+ [DataField("jobDepartments")]
+ [AutoNetworkedField]
+ public List<LocId> JobDepartments = new();
}
using Content.Shared.Medical.SuitSensor;
using Robust.Shared.Serialization;
-namespace Content.Shared.Medical.CrewMonitoring
+namespace Content.Shared.Medical.CrewMonitoring;
+
+[Serializable, NetSerializable]
+public enum CrewMonitoringUIKey
{
- [Serializable, NetSerializable]
- public enum CrewMonitoringUIKey
- {
- Key
- }
+ Key
+}
- [Serializable, NetSerializable]
- public sealed class CrewMonitoringState : BoundUserInterfaceState
- {
- public List<SuitSensorStatus> Sensors;
- public readonly bool Snap;
- public readonly float Precision;
+[Serializable, NetSerializable]
+public sealed class CrewMonitoringState : BoundUserInterfaceState
+{
+ public List<SuitSensorStatus> Sensors;
- public CrewMonitoringState(List<SuitSensorStatus> sensors, bool snap, float precision)
- {
- Sensors = sensors;
- Snap = snap;
- Precision = precision;
- }
+ public CrewMonitoringState(List<SuitSensorStatus> sensors)
+ {
+ Sensors = sensors;
}
-
}
using Robust.Shared.Map;
using Robust.Shared.Serialization;
-namespace Content.Shared.Medical.SuitSensor
+namespace Content.Shared.Medical.SuitSensor;
+
+[Serializable, NetSerializable]
+public sealed class SuitSensorStatus
{
- [Serializable, NetSerializable]
- public sealed class SuitSensorStatus
+ public SuitSensorStatus(NetEntity suitSensorUid, string name, string job, string jobIcon, List<string> jobDepartments)
{
- public SuitSensorStatus(NetEntity suitSensorUid, string name, string job)
- {
- SuitSensorUid = suitSensorUid;
- Name = name;
- Job = job;
- }
-
- public TimeSpan Timestamp;
- public NetEntity SuitSensorUid;
- public string Name;
- public string Job;
- public bool IsAlive;
- public int? TotalDamage;
- public NetCoordinates? Coordinates;
+ SuitSensorUid = suitSensorUid;
+ Name = name;
+ Job = job;
+ JobIcon = jobIcon;
+ JobDepartments = jobDepartments;
}
- [Serializable, NetSerializable]
- public enum SuitSensorMode : byte
- {
- /// <summary>
- /// Sensor doesn't send any information about owner
- /// </summary>
- SensorOff = 0,
-
- /// <summary>
- /// Sensor sends only binary status (alive/dead)
- /// </summary>
- SensorBinary = 1,
-
- /// <summary>
- /// Sensor sends health vitals status
- /// </summary>
- SensorVitals = 2,
-
- /// <summary>
- /// Sensor sends vitals status and GPS position
- /// </summary>
- SensorCords = 3
- }
+ public TimeSpan Timestamp;
+ public NetEntity SuitSensorUid;
+ public string Name;
+ public string Job;
+ public string JobIcon;
+ public List<string> JobDepartments;
+ public bool IsAlive;
+ public int? TotalDamage;
+ public NetCoordinates? Coordinates;
+}
- public static class SuitSensorConstants
- {
- public const string NET_NAME = "name";
- public const string NET_JOB = "job";
- public const string NET_IS_ALIVE = "alive";
- public const string NET_TOTAL_DAMAGE = "vitals";
- public const string NET_COORDINATES = "coords";
- public const string NET_SUIT_SENSOR_UID = "uid";
-
- ///Used by the CrewMonitoringServerSystem to send the status of all connected suit sensors to each crew monitor
- public const string NET_STATUS_COLLECTION = "suit-status-collection";
- }
+[Serializable, NetSerializable]
+public enum SuitSensorMode : byte
+{
+ /// <summary>
+ /// Sensor doesn't send any information about owner
+ /// </summary>
+ SensorOff = 0,
+
+ /// <summary>
+ /// Sensor sends only binary status (alive/dead)
+ /// </summary>
+ SensorBinary = 1,
+
+ /// <summary>
+ /// Sensor sends health vitals status
+ /// </summary>
+ SensorVitals = 2,
+
+ /// <summary>
+ /// Sensor sends vitals status and GPS position
+ /// </summary>
+ SensorCords = 3
+}
+
+public static class SuitSensorConstants
+{
+ public const string NET_NAME = "name";
+ public const string NET_JOB = "job";
+ public const string NET_JOB_ICON = "jobIcon";
+ public const string NET_JOB_DEPARTMENTS = "jobDepartments";
+ public const string NET_IS_ALIVE = "alive";
+ public const string NET_TOTAL_DAMAGE = "vitals";
+ public const string NET_COORDINATES = "coords";
+ public const string NET_SUIT_SENSOR_UID = "uid";
+
+ ///Used by the CrewMonitoringServerSystem to send the status of all connected suit sensors to each crew monitor
+ public const string NET_STATUS_COLLECTION = "suit-status-collection";
}
## UI
-crew-monitoring-user-interface-title = Crew Monitoring
+crew-monitoring-user-interface-title = Crew Monitoring Console
crew-monitoring-user-interface-name = Name
crew-monitoring-user-interface-job = Job
crew-monitoring-user-interface-no-info = N/A
crew-monitoring-user-interface-no-server = Server not found
+
+crew-monitoring-user-interface-no-department = Unknown
+
+crew-monitoring-user-interface-flavor-left = In case of an emergancy, contact station medical staff immediately
+crew-monitoring-user-interface-flavor-right = v1.7
\ No newline at end of file
--- /dev/null
+navmap-zoom = Zoom: {$value}%
+navmap-recenter = Recenter
+navmap-toggle-beacons = Show departments
\ No newline at end of file
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by chromiumboy, derived from https://github.com/tgstation/tgstation/commits/50689f89a40e5e7a2732a0c5fb38c787b69f7d28/icons/hud/screen_gen.dmi, ",
+ "size": {
+ "x": 24,
+ "y": 8
+ },
+ "states": [
+ {
+ "name": "alive"
+ },
+ {
+ "name": "dead"
+ },
+ {
+ "name": "health0"
+ },
+ {
+ "name": "health1"
+ },
+ {
+ "name": "health2"
+ },
+ {
+ "name": "health3"
+ },
+ {
+ "name": "health4"
+ },
+ {
+ "name": "critical",
+ "delays": [
+ [
+ 0.35,
+ 0.35
+ ]
+ ]
+ }
+ ]
+}