Power monitoring console overhaul (#20927)
* Prototyping whole station wire map * More prototyping * Added icons for the different power distributors and toggleable cable displays * Power cable layouts are now only sent to the client when the power monitor is open * UI prototyping * Power monitors can now see the sprites of distant entities, long entity names are truncated * Updated how network devices are added to the player's PVS * More feature prototypes * Added source / load symbols * Final prototype! Time to actually code it properly... * Start of code clean up * Continuing code clean up * Fixed UI appearance * Code clean up complete * Removed unnecessary changes * Updated how power values are calculated, added UI warnings for power sinks and power net checks * Updated how power values are calculated again, added support for portable generators * Removed unnecessary files * Map beacons start toggled off, console map now works outside the station, fixed substation icon * Made some of Sloth's requested changes. Power distributors don't blink anymore, unless selected * Moved a number of static variables in PowerMonitoringHelper to sensible places in the main files. Added a NavMapTrackableComponent so that you can specify how individual entities appear on the navmap * Updated the colors/positions of HV cables and SMESes to improve contrast * Fixed SMES color in map legend * Partially fixed auto-scrolling on device selection, made sublists alphabetical * Changed how auto-scroll is handled * Changed the font color of the console warning messages * Reduced the font size of beacon labels * Added the station name to the console * Organized references * Removed unwanted changes to RobustToolbox * Fix merge conflict * Fix merge conflict, maybe * Fix merge conflict * Updated outdated reference * Fixed portable_generator.yml * Implemented a number of requested changes, move bit masks to a shared component * Navigate listings via the navmap * First attempt at improving efficiency * Second attempt at optimization, entity grouping added for solar panels * Finished solar panel entity joining * Finished major revisions, code clean up needed * Finializing optimizations * Made requested changes * Bug fix, removed obsolete code * Bug fixes * Bug fixes * STarted revisions * Further revisions * More revision * Finalizing revisions. Need to make RT PR * Code tidying * More code tidying * Trying to avoid merge conflicts * Trying to avoid merge conflicts * Removed use of PVS * Improving efficiency * Addressed a bunch of outstanding issues * Clear old data on console refresh * UI adjustments * Made node comparison more robust. More devices can be combined into one entry * Added missing component 'dirty'
@@ -2,15 +2,15 @@
|
|||||||
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
|
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
Title="{Loc 'crew-monitoring-user-interface-title'}"
|
Title="{Loc 'crew-monitoring-user-interface-title'}"
|
||||||
SetSize="1200 700"
|
SetSize="1210 700"
|
||||||
MinSize="1200 700">
|
MinSize="1210 700">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
||||||
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
|
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<PanelContainer>
|
<PanelContainer>
|
||||||
<Label Name="StationName" Text="Unknown station" Align="Center" />
|
<Label Name="StationName" Text="Unknown station" Align="Center" Margin="0 5 0 3"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ public partial class NavMapControl : MapGridControl
|
|||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
private readonly SharedTransformSystem _transformSystem = default!;
|
private readonly SharedTransformSystem _transformSystem = default!;
|
||||||
|
|
||||||
|
public EntityUid? Owner;
|
||||||
public EntityUid? MapUid;
|
public EntityUid? MapUid;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
public event Action<NetEntity?>? TrackedEntitySelectedAction;
|
public event Action<NetEntity?>? TrackedEntitySelectedAction;
|
||||||
|
public event Action<DrawingHandleScreen>? PostWallDrawingAction;
|
||||||
|
|
||||||
// Tracked data
|
// Tracked data
|
||||||
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
|
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
|
||||||
@@ -358,6 +360,9 @@ public partial class NavMapControl : MapGridControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PostWallDrawingAction != null)
|
||||||
|
PostWallDrawingAction.Invoke(handle);
|
||||||
|
|
||||||
// Beacons
|
// Beacons
|
||||||
if (_beacons.Pressed)
|
if (_beacons.Pressed)
|
||||||
{
|
{
|
||||||
@@ -455,7 +460,7 @@ public partial class NavMapControl : MapGridControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateNavMap()
|
protected virtual void UpdateNavMap()
|
||||||
{
|
{
|
||||||
if (_navMap == null || _grid == null)
|
if (_navMap == null || _grid == null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using Content.Shared.Power;
|
||||||
|
|
||||||
|
namespace Content.Client.Power;
|
||||||
|
|
||||||
|
public sealed class PowerMonitoringConsoleBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
private PowerMonitoringWindow? _menu;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
_menu = new PowerMonitoringWindow(this, Owner);
|
||||||
|
_menu.OpenCentered();
|
||||||
|
_menu.OnClose += Close;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
var castState = (PowerMonitoringConsoleBoundInterfaceState) state;
|
||||||
|
|
||||||
|
if (castState == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||||
|
_menu?.ShowEntites
|
||||||
|
(castState.TotalSources,
|
||||||
|
castState.TotalBatteryUsage,
|
||||||
|
castState.TotalLoads,
|
||||||
|
castState.AllEntries,
|
||||||
|
castState.FocusSources,
|
||||||
|
castState.FocusLoads,
|
||||||
|
xform?.Coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendPowerMonitoringConsoleMessage(NetEntity? netEntity, PowerMonitoringConsoleGroup group)
|
||||||
|
{
|
||||||
|
SendMessage(new PowerMonitoringConsoleMessage(netEntity, group));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_menu?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
300
Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
using Content.Client.Pinpointer.UI;
|
||||||
|
using Content.Shared.Pinpointer;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Content.Client.Power;
|
||||||
|
|
||||||
|
public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
|
||||||
|
// Cable indexing
|
||||||
|
// 0: CableType.HighVoltage
|
||||||
|
// 1: CableType.MediumVoltage
|
||||||
|
// 2: CableType.Apc
|
||||||
|
|
||||||
|
private readonly Color[] _powerCableColors = { Color.OrangeRed, Color.Yellow, Color.LimeGreen };
|
||||||
|
private readonly Vector2[] _powerCableOffsets = { new Vector2(-0.2f, -0.2f), Vector2.Zero, new Vector2(0.2f, 0.2f) };
|
||||||
|
private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
|
||||||
|
|
||||||
|
public PowerMonitoringCableNetworksComponent? PowerMonitoringCableNetworks;
|
||||||
|
public List<PowerMonitoringConsoleLineGroup> HiddenLineGroups = new();
|
||||||
|
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? PowerCableNetwork;
|
||||||
|
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? FocusCableNetwork;
|
||||||
|
|
||||||
|
private MapGridComponent? _grid;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleNavMapControl() : base()
|
||||||
|
{
|
||||||
|
// Set colors
|
||||||
|
TileColor = new Color(30, 57, 67);
|
||||||
|
WallColor = new Color(102, 164, 217);
|
||||||
|
|
||||||
|
PostWallDrawingAction += DrawAllCableNetworks;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateNavMap()
|
||||||
|
{
|
||||||
|
base.UpdateNavMap();
|
||||||
|
|
||||||
|
if (Owner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<PowerMonitoringCableNetworksComponent>(Owner, out var cableNetworks))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent(MapUid, out _grid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PowerCableNetwork = GetDecodedPowerCableChunks(cableNetworks.AllChunks, _grid);
|
||||||
|
FocusCableNetwork = GetDecodedPowerCableChunks(cableNetworks.FocusChunks, _grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawAllCableNetworks(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
// Draw full cable network
|
||||||
|
if (PowerCableNetwork != null && PowerCableNetwork.Count > 0)
|
||||||
|
{
|
||||||
|
var modulator = (FocusCableNetwork != null && FocusCableNetwork.Count > 0) ? Color.DimGray : Color.White;
|
||||||
|
DrawCableNetwork(handle, PowerCableNetwork, modulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw focus network
|
||||||
|
if (FocusCableNetwork != null && FocusCableNetwork.Count > 0)
|
||||||
|
DrawCableNetwork(handle, FocusCableNetwork, Color.White);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawCableNetwork(DrawingHandleScreen handle, Dictionary<Vector2i, List<PowerMonitoringConsoleLine>> fullCableNetwork, Color modulator)
|
||||||
|
{
|
||||||
|
var offset = GetOffset();
|
||||||
|
var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset);
|
||||||
|
|
||||||
|
if (WorldRange / WorldMaxRange > 0.5f)
|
||||||
|
{
|
||||||
|
var cableNetworks = new ValueList<Vector2>[3];
|
||||||
|
|
||||||
|
foreach ((var chunk, var chunkedLines) in fullCableNetwork)
|
||||||
|
{
|
||||||
|
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||||
|
|
||||||
|
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var chunkedLine in chunkedLines)
|
||||||
|
{
|
||||||
|
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var start = Scale(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||||
|
var end = Scale(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
|
cableNetworks[(int) chunkedLine.Group].Add(start);
|
||||||
|
cableNetworks[(int) chunkedLine.Group].Add(end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int cableNetworkIdx = 0; cableNetworkIdx < cableNetworks.Length; cableNetworkIdx++)
|
||||||
|
{
|
||||||
|
var cableNetwork = cableNetworks[cableNetworkIdx];
|
||||||
|
|
||||||
|
if (cableNetwork.Count > 0)
|
||||||
|
{
|
||||||
|
var color = _powerCableColors[cableNetworkIdx] * modulator;
|
||||||
|
|
||||||
|
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||||
|
{
|
||||||
|
sRGB = Color.ToSrgb(color);
|
||||||
|
_sRGBLookUp[color] = sRGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, cableNetwork.Span, sRGB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var cableVertexUVs = new ValueList<Vector2>[3];
|
||||||
|
|
||||||
|
foreach ((var chunk, var chunkedLines) in fullCableNetwork)
|
||||||
|
{
|
||||||
|
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||||
|
|
||||||
|
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var chunkedLine in chunkedLines)
|
||||||
|
{
|
||||||
|
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var leftTop = Scale(new Vector2
|
||||||
|
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||||
|
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||||
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
|
var rightTop = Scale(new Vector2
|
||||||
|
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||||
|
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||||
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
|
var leftBottom = Scale(new Vector2
|
||||||
|
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||||
|
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||||
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
|
var rightBottom = Scale(new Vector2
|
||||||
|
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||||
|
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||||
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(leftBottom);
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(leftTop);
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(rightBottom);
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(leftTop);
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(rightBottom);
|
||||||
|
cableVertexUVs[(int) chunkedLine.Group].Add(rightTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int cableNetworkIdx = 0; cableNetworkIdx < cableVertexUVs.Length; cableNetworkIdx++)
|
||||||
|
{
|
||||||
|
var cableVertexUV = cableVertexUVs[cableNetworkIdx];
|
||||||
|
|
||||||
|
if (cableVertexUV.Count > 0)
|
||||||
|
{
|
||||||
|
var color = _powerCableColors[cableNetworkIdx] * modulator;
|
||||||
|
|
||||||
|
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||||
|
{
|
||||||
|
sRGB = Color.ToSrgb(color);
|
||||||
|
_sRGBLookUp[color] = sRGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, cableVertexUV.Span, sRGB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? GetDecodedPowerCableChunks(Dictionary<Vector2i, PowerCableChunk>? chunks, MapGridComponent? grid)
|
||||||
|
{
|
||||||
|
if (chunks == null || grid == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var decodedOutput = new Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>();
|
||||||
|
|
||||||
|
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||||
|
{
|
||||||
|
var list = new List<PowerMonitoringConsoleLine>();
|
||||||
|
|
||||||
|
for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
|
||||||
|
{
|
||||||
|
var chunkMask = chunk.PowerCableData[cableIdx];
|
||||||
|
|
||||||
|
Vector2 offset = _powerCableOffsets[cableIdx];
|
||||||
|
|
||||||
|
for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
|
||||||
|
{
|
||||||
|
var value = (int) Math.Pow(2, chunkIdx);
|
||||||
|
var mask = chunkMask & value;
|
||||||
|
|
||||||
|
if (mask == 0x0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||||
|
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize;
|
||||||
|
var position = new Vector2(tile.X, -tile.Y);
|
||||||
|
|
||||||
|
PowerCableChunk neighborChunk;
|
||||||
|
bool neighbor;
|
||||||
|
|
||||||
|
// Note: we only check the north and east neighbors
|
||||||
|
|
||||||
|
// East
|
||||||
|
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||||
|
{
|
||||||
|
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||||
|
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||||
|
neighbor = (chunkMask & flag) != 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor)
|
||||||
|
{
|
||||||
|
// Add points
|
||||||
|
var line = new PowerMonitoringConsoleLine
|
||||||
|
(position + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||||
|
position + new Vector2(1f, 0f) + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||||
|
(PowerMonitoringConsoleLineGroup) cableIdx);
|
||||||
|
|
||||||
|
list.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// North
|
||||||
|
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||||
|
{
|
||||||
|
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||||
|
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||||
|
neighbor = (chunkMask & flag) != 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor)
|
||||||
|
{
|
||||||
|
// Add points
|
||||||
|
var line = new PowerMonitoringConsoleLine
|
||||||
|
(position + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||||
|
position + new Vector2(0f, -1f) + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||||
|
(PowerMonitoringConsoleLineGroup) cableIdx);
|
||||||
|
|
||||||
|
list.Add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.Count > 0)
|
||||||
|
decodedOutput.Add(chunkOrigin, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PowerMonitoringConsoleLine
|
||||||
|
{
|
||||||
|
public readonly Vector2 Origin;
|
||||||
|
public readonly Vector2 Terminus;
|
||||||
|
public readonly PowerMonitoringConsoleLineGroup Group;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleLine(Vector2 origin, Vector2 terminus, PowerMonitoringConsoleLineGroup group)
|
||||||
|
{
|
||||||
|
Origin = origin;
|
||||||
|
Terminus = terminus;
|
||||||
|
Group = group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PowerMonitoringConsoleLineGroup : byte
|
||||||
|
{
|
||||||
|
HighVoltage,
|
||||||
|
MediumVoltage,
|
||||||
|
Apc,
|
||||||
|
}
|
||||||
@@ -1,20 +1,117 @@
|
|||||||
<DefaultWindow
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns="https://spacestation14.io"
|
xmlns:ui="clr-namespace:Content.Client.Power"
|
||||||
Title="{Loc 'power-monitoring-window-title'}">
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
<GridContainer Columns="2">
|
Title="{Loc 'power-monitoring-window-title'}"
|
||||||
<!-- Grid is used here to align things. -->
|
SetSize="1120 750"
|
||||||
<Label Text="{Loc 'power-monitoring-window-total-sources'}"/><Label Name="TotalSourcesNum" Text="?"/>
|
MinSize="1120 750">
|
||||||
<Label Text="{Loc 'power-monitoring-window-total-loads'}"/><Label Name="TotalLoadsNum" Text="?"/>
|
<BoxContainer Orientation="Vertical">
|
||||||
</GridContainer>
|
<!-- Main display -->
|
||||||
<TabContainer Name="MasterTabContainer" VerticalExpand="True">
|
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
||||||
<ItemList Name="SourcesList" VerticalExpand="True">
|
<!-- Nav map -->
|
||||||
</ItemList>
|
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
<ui:PowerMonitoringConsoleNavMapControl Name="NavMap" Margin="5 5" VerticalExpand="True" HorizontalExpand="True">
|
||||||
<CheckBox Margin="8 8" Name="ShowInactiveConsumersCheckBox" Text="{Loc 'power-monitoring-window-show-inactive-consumers'}" />
|
|
||||||
<ItemList Name="LoadsList" VerticalExpand="True">
|
<!-- System warning -->
|
||||||
</ItemList>
|
<PanelContainer Name="SystemWarningPanel"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="0 48 0 0"
|
||||||
|
Visible="False">
|
||||||
|
<RichTextLabel Name="SystemWarningLabel" Margin="12 8 12 8"/>
|
||||||
|
</PanelContainer>
|
||||||
|
|
||||||
|
|
||||||
|
</ui:PowerMonitoringConsoleNavMapControl>
|
||||||
|
|
||||||
|
<!-- Nav map legend -->
|
||||||
|
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
|
||||||
|
Modulate="#800080"
|
||||||
|
SetSize="16 16"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-label-sources'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_hexagon.png"
|
||||||
|
SetSize="16 16"
|
||||||
|
Modulate="#ff4500"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-label-smes'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
|
||||||
|
SetSize="16 16"
|
||||||
|
Modulate="#ffff00"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-label-substation'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_triangle.png"
|
||||||
|
SetSize="16 16"
|
||||||
|
Modulate="#32cd32"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-label-apc'}"/>
|
||||||
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</TabContainer>
|
|
||||||
|
<!-- Power status -->
|
||||||
|
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="440" Margin="0 0 10 10">
|
||||||
|
|
||||||
|
<!-- Station name -->
|
||||||
|
<controls:StripeBack>
|
||||||
|
<PanelContainer>
|
||||||
|
<RichTextLabel Name="StationName" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 3"/>
|
||||||
|
</PanelContainer>
|
||||||
|
</controls:StripeBack>
|
||||||
|
|
||||||
|
<!-- Power overview -->
|
||||||
|
<GridContainer Columns="2">
|
||||||
|
<Label StyleClasses="StatusFieldTitle" Text="{Loc 'power-monitoring-window-total-sources'}"/>
|
||||||
|
<Label Name="TotalSources" Text="?" Margin="10 0 0 0"/>
|
||||||
|
<Label StyleClasses="StatusFieldTitle" Text="{Loc 'power-monitoring-window-total-battery-usage'}"/>
|
||||||
|
<Label Name="TotalBatteryUsage" Text="?" Margin="10 0 0 0"/>
|
||||||
|
<Label StyleClasses="StatusFieldTitle" Text="{Loc 'power-monitoring-window-total-loads'}"/>
|
||||||
|
<Label Name="TotalLoads" Text="?" Margin="10 0 0 0"/>
|
||||||
|
</GridContainer>
|
||||||
|
|
||||||
|
<!-- Loads / Sources (entries added by C# code) -->
|
||||||
|
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="SourcesList" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 10 0"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="SMESList" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 10 0"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="SubstationList" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 10 0"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="ApcList" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 10 0"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
</TabContainer>
|
||||||
|
|
||||||
|
<!-- Cable network toggles -->
|
||||||
|
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-show-cable-networks'}" Margin="0 0 0 5"/>
|
||||||
|
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||||
|
<CheckBox Name="ShowHVCable" Text="{Loc 'power-monitoring-window-show-hv-cable'}" Pressed="True" Margin="0 0 0 0"/>
|
||||||
|
<CheckBox Name="ShowMVCable" Text="{Loc 'power-monitoring-window-show-mv-cable'}" Pressed="True" Margin="10 0 0 0"/>
|
||||||
|
<CheckBox Name="ShowLVCable" Text="{Loc 'power-monitoring-window-show-lv-cable'}" Pressed="True" Margin="10 0 0 0"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<PanelContainer StyleClasses="LowDivider" />
|
||||||
|
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||||
|
<Label Text="{Loc 'power-monitoring-window-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>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</controls:FancyWindow>
|
||||||
|
|||||||
490
Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Content.Client.Power;
|
||||||
|
|
||||||
|
public sealed partial class PowerMonitoringWindow
|
||||||
|
{
|
||||||
|
private SpriteSpecifier.Texture _sourceIcon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/PowerMonitoring/source_arrow.png"));
|
||||||
|
private SpriteSpecifier.Texture _loadIconPath = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/PowerMonitoring/load_arrow.png"));
|
||||||
|
|
||||||
|
private bool _autoScrollActive = false;
|
||||||
|
private bool _autoScrollAwaitsUpdate = false;
|
||||||
|
|
||||||
|
private void UpdateWindowConsoleEntry
|
||||||
|
(BoxContainer masterContainer,
|
||||||
|
int index,
|
||||||
|
PowerMonitoringConsoleEntry entry,
|
||||||
|
PowerMonitoringConsoleEntry[] focusSources,
|
||||||
|
PowerMonitoringConsoleEntry[] focusLoads)
|
||||||
|
{
|
||||||
|
UpdateWindowConsoleEntry(masterContainer, index, entry);
|
||||||
|
|
||||||
|
var windowEntry = masterContainer.GetChild(index) as PowerMonitoringWindowEntry;
|
||||||
|
|
||||||
|
// If we exit here, something was added to the container that shouldn't have been added
|
||||||
|
if (windowEntry == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update sources and loads
|
||||||
|
UpdateEntrySourcesOrLoads(masterContainer, windowEntry.SourcesContainer, focusSources, _sourceIcon);
|
||||||
|
UpdateEntrySourcesOrLoads(masterContainer, windowEntry.LoadsContainer, focusLoads, _loadIconPath);
|
||||||
|
|
||||||
|
windowEntry.MainContainer.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateWindowConsoleEntry(BoxContainer masterContainer, int index, PowerMonitoringConsoleEntry entry)
|
||||||
|
{
|
||||||
|
PowerMonitoringWindowEntry? windowEntry;
|
||||||
|
|
||||||
|
// Add missing children
|
||||||
|
if (index >= masterContainer.ChildCount)
|
||||||
|
{
|
||||||
|
// Add basic entry
|
||||||
|
windowEntry = new PowerMonitoringWindowEntry(entry);
|
||||||
|
masterContainer.AddChild(windowEntry);
|
||||||
|
|
||||||
|
// Selection action
|
||||||
|
windowEntry.Button.OnButtonUp += args =>
|
||||||
|
{
|
||||||
|
windowEntry.SourcesContainer.DisposeAllChildren();
|
||||||
|
windowEntry.LoadsContainer.DisposeAllChildren();
|
||||||
|
ButtonAction(windowEntry, masterContainer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
windowEntry = masterContainer.GetChild(index) as PowerMonitoringWindowEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we exit here, something was added to the container that shouldn't have been added
|
||||||
|
if (windowEntry == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
windowEntry.NetEntity = entry.NetEntity;
|
||||||
|
windowEntry.Entry = entry;
|
||||||
|
windowEntry.MainContainer.Visible = false;
|
||||||
|
|
||||||
|
UpdateWindowEntryButton(entry.NetEntity, windowEntry.Button, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateWindowEntryButton(NetEntity netEntity, PowerMonitoringButton button, PowerMonitoringConsoleEntry entry)
|
||||||
|
{
|
||||||
|
if (!netEntity.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entry.MetaData == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update button style
|
||||||
|
if (netEntity == _focusEntity)
|
||||||
|
button.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
|
||||||
|
else
|
||||||
|
button.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
|
||||||
|
// Update sprite
|
||||||
|
if (entry.MetaData.Value.SpritePath != string.Empty && entry.MetaData.Value.SpriteState != string.Empty)
|
||||||
|
button.TextureRect.Texture = _spriteSystem.Frame0(new SpriteSpecifier.Rsi(new ResPath(entry.MetaData.Value.SpritePath), entry.MetaData.Value.SpriteState));
|
||||||
|
|
||||||
|
// Update name
|
||||||
|
var name = Loc.GetString(entry.MetaData.Value.EntityName);
|
||||||
|
button.NameLocalized.Text = name;
|
||||||
|
|
||||||
|
// Update tool tip
|
||||||
|
button.ToolTip = Loc.GetString(name);
|
||||||
|
|
||||||
|
// Update power value
|
||||||
|
button.PowerValue.Text = Loc.GetString("power-monitoring-window-value", ("value", entry.PowerValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon)
|
||||||
|
{
|
||||||
|
if (currentContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entries == null || entries.Length == 0)
|
||||||
|
{
|
||||||
|
currentContainer.RemoveAllChildren();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove excess children
|
||||||
|
while (currentContainer.ChildCount > entries.Length)
|
||||||
|
{
|
||||||
|
currentContainer.RemoveChild(currentContainer.GetChild(currentContainer.ChildCount - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add missing children
|
||||||
|
while (currentContainer.ChildCount < entries.Length)
|
||||||
|
{
|
||||||
|
var entry = entries[currentContainer.ChildCount];
|
||||||
|
var subEntry = new PowerMonitoringWindowSubEntry(entry);
|
||||||
|
currentContainer.AddChild(subEntry);
|
||||||
|
|
||||||
|
// Selection action
|
||||||
|
subEntry.Button.OnButtonUp += args => { ButtonAction(subEntry, masterContainer); };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<PowerMonitoringConsoleComponent>(_owner, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update all children
|
||||||
|
foreach (var child in currentContainer.Children)
|
||||||
|
{
|
||||||
|
if (child is not PowerMonitoringWindowSubEntry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var castChild = (PowerMonitoringWindowSubEntry) child;
|
||||||
|
|
||||||
|
if (castChild == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (castChild.Icon != null)
|
||||||
|
castChild.Icon.Texture = _spriteSystem.Frame0(icon);
|
||||||
|
|
||||||
|
var entry = entries[child.GetPositionInParent()];
|
||||||
|
|
||||||
|
castChild.NetEntity = entry.NetEntity;
|
||||||
|
castChild.Entry = entry;
|
||||||
|
|
||||||
|
UpdateWindowEntryButton(entry.NetEntity, castChild.Button, entries.ElementAt(child.GetPositionInParent()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonAction(PowerMonitoringWindowBaseEntry entry, BoxContainer masterContainer)
|
||||||
|
{
|
||||||
|
// Toggle off button?
|
||||||
|
if (entry.NetEntity == _focusEntity)
|
||||||
|
{
|
||||||
|
entry.Button.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
_focusEntity = null;
|
||||||
|
|
||||||
|
// Request an update from the power monitoring system
|
||||||
|
SendPowerMonitoringConsoleMessageAction?.Invoke(null, entry.Entry.Group);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, toggle on
|
||||||
|
entry.Button.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
|
||||||
|
ActivateAutoScrollToFocus();
|
||||||
|
|
||||||
|
// Toggle off the old button (if applicable)
|
||||||
|
if (_focusEntity != null)
|
||||||
|
{
|
||||||
|
foreach (PowerMonitoringWindowEntry sibling in masterContainer.Children)
|
||||||
|
{
|
||||||
|
if (sibling.NetEntity == _focusEntity)
|
||||||
|
{
|
||||||
|
sibling.Button.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center the nav map on selected entity
|
||||||
|
_focusEntity = entry.NetEntity;
|
||||||
|
|
||||||
|
if (!NavMap.TrackedEntities.TryGetValue(entry.NetEntity, out var blip))
|
||||||
|
return;
|
||||||
|
|
||||||
|
NavMap.CenterToCoordinates(blip.Coordinates);
|
||||||
|
|
||||||
|
// Switch tabs
|
||||||
|
SwitchTabsBasedOnPowerMonitoringConsoleGroup(entry.Entry.Group);
|
||||||
|
|
||||||
|
// Send an update from the power monitoring system
|
||||||
|
SendPowerMonitoringConsoleMessageAction?.Invoke(_focusEntity, entry.Entry.Group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ActivateAutoScrollToFocus()
|
||||||
|
{
|
||||||
|
_autoScrollActive = false;
|
||||||
|
_autoScrollAwaitsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
|
||||||
|
{
|
||||||
|
nextScrollPosition = null;
|
||||||
|
|
||||||
|
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer;
|
||||||
|
if (scroll == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var container = scroll.Children.ElementAt(0) as BoxContainer;
|
||||||
|
if (container == null || container.Children.Count() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Exit if the heights of the children haven't been initialized yet
|
||||||
|
if (!container.Children.Any(x => x.Height > 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nextScrollPosition = 0;
|
||||||
|
|
||||||
|
foreach (var control in container.Children)
|
||||||
|
{
|
||||||
|
if (control == null || control is not PowerMonitoringWindowEntry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (((PowerMonitoringWindowEntry) control).NetEntity == _focusEntity)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
nextScrollPosition += control.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to find control
|
||||||
|
nextScrollPosition = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
|
||||||
|
{
|
||||||
|
vScrollBar = null;
|
||||||
|
|
||||||
|
foreach (var child in scroll.Children)
|
||||||
|
{
|
||||||
|
if (child is not VScrollBar)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var castChild = child as VScrollBar;
|
||||||
|
|
||||||
|
if (castChild != null)
|
||||||
|
{
|
||||||
|
vScrollBar = castChild;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutoScrollToFocus()
|
||||||
|
{
|
||||||
|
if (!_autoScrollActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer;
|
||||||
|
if (scroll == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryGetVerticalScrollbar(scroll, out var vScrollbar))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryGetNextScrollPosition(out float? nextScrollPosition))
|
||||||
|
return;
|
||||||
|
|
||||||
|
vScrollbar.ValueTarget = nextScrollPosition.Value;
|
||||||
|
|
||||||
|
if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
|
||||||
|
_autoScrollActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateWarningLabel(PowerMonitoringFlags flags)
|
||||||
|
{
|
||||||
|
if (flags == PowerMonitoringFlags.None)
|
||||||
|
{
|
||||||
|
SystemWarningPanel.Visible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = new FormattedMessage();
|
||||||
|
|
||||||
|
if ((flags & PowerMonitoringFlags.RoguePowerConsumer) != 0)
|
||||||
|
{
|
||||||
|
SystemWarningPanel.PanelOverride = new StyleBoxFlat
|
||||||
|
{
|
||||||
|
BackgroundColor = Color.Red,
|
||||||
|
BorderColor = Color.DarkRed,
|
||||||
|
BorderThickness = new Thickness(2),
|
||||||
|
};
|
||||||
|
|
||||||
|
msg.AddMarkup(Loc.GetString("power-monitoring-window-rogue-power-consumer"));
|
||||||
|
SystemWarningPanel.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((flags & PowerMonitoringFlags.PowerNetAbnormalities) != 0)
|
||||||
|
{
|
||||||
|
SystemWarningPanel.PanelOverride = new StyleBoxFlat
|
||||||
|
{
|
||||||
|
BackgroundColor = Color.Orange,
|
||||||
|
BorderColor = Color.DarkOrange,
|
||||||
|
BorderThickness = new Thickness(2),
|
||||||
|
};
|
||||||
|
|
||||||
|
msg.AddMarkup(Loc.GetString("power-monitoring-window-power-net-abnormalities"));
|
||||||
|
SystemWarningPanel.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemWarningLabel.SetMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SwitchTabsBasedOnPowerMonitoringConsoleGroup(PowerMonitoringConsoleGroup group)
|
||||||
|
{
|
||||||
|
switch (group)
|
||||||
|
{
|
||||||
|
case PowerMonitoringConsoleGroup.Generator:
|
||||||
|
MasterTabContainer.CurrentTab = 0; break;
|
||||||
|
case PowerMonitoringConsoleGroup.SMES:
|
||||||
|
MasterTabContainer.CurrentTab = 1; break;
|
||||||
|
case PowerMonitoringConsoleGroup.Substation:
|
||||||
|
MasterTabContainer.CurrentTab = 2; break;
|
||||||
|
case PowerMonitoringConsoleGroup.APC:
|
||||||
|
MasterTabContainer.CurrentTab = 3; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PowerMonitoringConsoleGroup GetCurrentPowerMonitoringConsoleGroup()
|
||||||
|
{
|
||||||
|
return (PowerMonitoringConsoleGroup) MasterTabContainer.CurrentTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PowerMonitoringWindowEntry : PowerMonitoringWindowBaseEntry
|
||||||
|
{
|
||||||
|
public BoxContainer MainContainer;
|
||||||
|
public BoxContainer SourcesContainer;
|
||||||
|
public BoxContainer LoadsContainer;
|
||||||
|
|
||||||
|
public PowerMonitoringWindowEntry(PowerMonitoringConsoleEntry entry) : base(entry)
|
||||||
|
{
|
||||||
|
Entry = entry;
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
Orientation = LayoutOrientation.Vertical;
|
||||||
|
HorizontalExpand = true;
|
||||||
|
|
||||||
|
// Update selection button
|
||||||
|
Button.StyleClasses.Add("OpenLeft");
|
||||||
|
AddChild(Button);
|
||||||
|
|
||||||
|
// Grid container to hold sub containers
|
||||||
|
MainContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Margin = new Thickness(8, 0, 0, 0),
|
||||||
|
Visible = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(MainContainer);
|
||||||
|
|
||||||
|
// Grid container to hold the list of sources when selected
|
||||||
|
SourcesContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
MainContainer.AddChild(SourcesContainer);
|
||||||
|
|
||||||
|
// Grid container to hold the list of loads when selected
|
||||||
|
LoadsContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
MainContainer.AddChild(LoadsContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PowerMonitoringWindowSubEntry : PowerMonitoringWindowBaseEntry
|
||||||
|
{
|
||||||
|
public TextureRect? Icon;
|
||||||
|
|
||||||
|
public PowerMonitoringWindowSubEntry(PowerMonitoringConsoleEntry entry) : base(entry)
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Horizontal;
|
||||||
|
HorizontalExpand = true;
|
||||||
|
|
||||||
|
// Source/load icon
|
||||||
|
Icon = new TextureRect()
|
||||||
|
{
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
Margin = new Thickness(0, 0, 2, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(Icon);
|
||||||
|
|
||||||
|
// Selection button
|
||||||
|
Button.StyleClasses.Add("OpenBoth");
|
||||||
|
AddChild(Button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class PowerMonitoringWindowBaseEntry : BoxContainer
|
||||||
|
{
|
||||||
|
public NetEntity NetEntity;
|
||||||
|
public PowerMonitoringConsoleEntry Entry;
|
||||||
|
public PowerMonitoringButton Button;
|
||||||
|
|
||||||
|
public PowerMonitoringWindowBaseEntry(PowerMonitoringConsoleEntry entry)
|
||||||
|
{
|
||||||
|
Entry = entry;
|
||||||
|
|
||||||
|
// Add selection button (properties set by derivative classes)
|
||||||
|
Button = new PowerMonitoringButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PowerMonitoringButton : Button
|
||||||
|
{
|
||||||
|
public BoxContainer MainContainer;
|
||||||
|
public TextureRect TextureRect;
|
||||||
|
public Label NameLocalized;
|
||||||
|
public Label PowerValue;
|
||||||
|
|
||||||
|
public PowerMonitoringButton()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true;
|
||||||
|
VerticalExpand = true;
|
||||||
|
Margin = new Thickness(0f, 1f, 0f, 1f);
|
||||||
|
|
||||||
|
MainContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
SetHeight = 32f,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(MainContainer);
|
||||||
|
|
||||||
|
TextureRect = new TextureRect()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
SetSize = new Vector2(32f, 32f),
|
||||||
|
Margin = new Thickness(0f, 0f, 5f, 0f),
|
||||||
|
};
|
||||||
|
|
||||||
|
MainContainer.AddChild(TextureRect);
|
||||||
|
|
||||||
|
NameLocalized = new Label()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
ClipText = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
MainContainer.AddChild(NameLocalized);
|
||||||
|
|
||||||
|
PowerValue = new Label()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Right,
|
||||||
|
SetWidth = 72f,
|
||||||
|
Margin = new Thickness(10, 0, 0, 0),
|
||||||
|
ClipText = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
MainContainer.AddChild(PowerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +1,317 @@
|
|||||||
using System.Linq;
|
using Content.Client.Pinpointer.UI;
|
||||||
using System.Numerics;
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Client.Computer;
|
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Graphics;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Graphics.RSI;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Utility;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Content.Client.Power;
|
namespace Content.Client.Power;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class PowerMonitoringWindow : DefaultWindow, IComputerWindow<PowerMonitoringConsoleBoundInterfaceState>
|
public sealed partial class PowerMonitoringWindow : FancyWindow
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
private readonly IEntityManager _entManager;
|
||||||
private readonly SpriteSystem _spriteSystem = default!;
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
private readonly IGameTiming _gameTiming;
|
||||||
|
|
||||||
public PowerMonitoringWindow()
|
private const float BlinkFrequency = 1f;
|
||||||
|
|
||||||
|
private EntityUid? _owner;
|
||||||
|
private NetEntity? _focusEntity;
|
||||||
|
|
||||||
|
public event Action<NetEntity?, PowerMonitoringConsoleGroup>? SendPowerMonitoringConsoleMessageAction;
|
||||||
|
|
||||||
|
private Dictionary<PowerMonitoringConsoleGroup, (SpriteSpecifier.Texture, Color)> _groupBlips = new()
|
||||||
|
{
|
||||||
|
{ PowerMonitoringConsoleGroup.Generator, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), Color.Purple) },
|
||||||
|
{ PowerMonitoringConsoleGroup.SMES, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_hexagon.png")), Color.OrangeRed) },
|
||||||
|
{ PowerMonitoringConsoleGroup.Substation, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_square.png")), Color.Yellow) },
|
||||||
|
{ PowerMonitoringConsoleGroup.APC, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), Color.LimeGreen) },
|
||||||
|
};
|
||||||
|
|
||||||
|
public PowerMonitoringWindow(PowerMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
SetSize = MinSize = new Vector2(300, 450);
|
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
IoCManager.InjectDependencies(this);
|
_gameTiming = IoCManager.Resolve<IGameTiming>();
|
||||||
_spriteSystem = IoCManager.Resolve<IEntityManager>().System<SpriteSystem>();
|
|
||||||
MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-tab-sources"));
|
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||||
MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-tab-loads"));
|
_owner = owner;
|
||||||
|
|
||||||
|
// Pass owner to nav map
|
||||||
|
NavMap.Owner = _owner;
|
||||||
|
|
||||||
|
// Set nav map grid uid
|
||||||
|
var stationName = Loc.GetString("power-monitoring-window-unknown-location");
|
||||||
|
|
||||||
|
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||||
|
{
|
||||||
|
NavMap.MapUid = xform.GridUid;
|
||||||
|
|
||||||
|
// Assign station name
|
||||||
|
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
|
||||||
|
stationName = stationMetaData.EntityName;
|
||||||
|
|
||||||
|
var msg = new FormattedMessage();
|
||||||
|
msg.AddMarkup(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName)));
|
||||||
|
|
||||||
|
StationName.SetMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StationName.SetMessage(stationName);
|
||||||
|
NavMap.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set trackable entity selected action
|
||||||
|
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
||||||
|
|
||||||
|
// Update nav map
|
||||||
|
NavMap.ForceNavMapUpdate();
|
||||||
|
|
||||||
|
// Set UI tab titles
|
||||||
|
MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources"));
|
||||||
|
MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes"));
|
||||||
|
MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation"));
|
||||||
|
MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc"));
|
||||||
|
|
||||||
|
// Track when the MasterTabContainer changes its tab
|
||||||
|
MasterTabContainer.OnTabChanged += OnTabChanged;
|
||||||
|
|
||||||
|
// Set UI toggles
|
||||||
|
ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage);
|
||||||
|
ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage);
|
||||||
|
ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc);
|
||||||
|
|
||||||
|
// Set power monitoring message action
|
||||||
|
SendPowerMonitoringConsoleMessageAction += userInterface.SendPowerMonitoringConsoleMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateState(PowerMonitoringConsoleBoundInterfaceState scc)
|
private void OnTabChanged(int tab)
|
||||||
{
|
{
|
||||||
UpdateList(TotalSourcesNum, scc.TotalSources, SourcesList, scc.Sources);
|
SendPowerMonitoringConsoleMessageAction?.Invoke(_focusEntity, (PowerMonitoringConsoleGroup) tab);
|
||||||
var loads = scc.Loads;
|
|
||||||
if (!ShowInactiveConsumersCheckBox.Pressed)
|
|
||||||
{
|
|
||||||
// Not showing inactive consumers, so hiding them.
|
|
||||||
// This means filtering out loads that are not either:
|
|
||||||
// + Batteries (always important)
|
|
||||||
// + Meaningful (size above 0)
|
|
||||||
loads = loads.Where(a => a.IsBattery || a.Size > 0.0f).ToArray();
|
|
||||||
}
|
|
||||||
UpdateList(TotalLoadsNum, scc.TotalLoads, LoadsList, loads);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateList(Label number, double numberVal, ItemList list, PowerMonitoringConsoleEntry[] listVal)
|
private void OnShowCableToggled(PowerMonitoringConsoleLineGroup lineGroup)
|
||||||
{
|
{
|
||||||
number.Text = Loc.GetString("power-monitoring-window-value", ("value", numberVal));
|
if (!NavMap.HiddenLineGroups.Remove(lineGroup))
|
||||||
// This magic is important to prevent scrolling issues.
|
NavMap.HiddenLineGroups.Add(lineGroup);
|
||||||
while (list.Count > listVal.Length)
|
}
|
||||||
|
|
||||||
|
public void ShowEntites
|
||||||
|
(double totalSources,
|
||||||
|
double totalBatteryUsage,
|
||||||
|
double totalLoads,
|
||||||
|
PowerMonitoringConsoleEntry[] allEntries,
|
||||||
|
PowerMonitoringConsoleEntry[] focusSources,
|
||||||
|
PowerMonitoringConsoleEntry[] focusLoads,
|
||||||
|
EntityCoordinates? monitorCoords)
|
||||||
|
{
|
||||||
|
if (_owner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<PowerMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update power status text
|
||||||
|
TotalSources.Text = Loc.GetString("power-monitoring-window-value", ("value", totalSources));
|
||||||
|
TotalBatteryUsage.Text = Loc.GetString("power-monitoring-window-value", ("value", totalBatteryUsage));
|
||||||
|
TotalLoads.Text = Loc.GetString("power-monitoring-window-value", ("value", totalLoads));
|
||||||
|
|
||||||
|
// 10+% of station power is being drawn from batteries
|
||||||
|
TotalBatteryUsage.FontColorOverride = (totalSources * 0.1111f) < totalBatteryUsage ? new Color(180, 0, 0) : Color.White;
|
||||||
|
|
||||||
|
// Station generator and battery output is less than the current demand
|
||||||
|
TotalLoads.FontColorOverride = (totalSources + totalBatteryUsage) < totalLoads &&
|
||||||
|
!MathHelper.CloseToPercent(totalSources + totalBatteryUsage, totalLoads, 0.1f) ? new Color(180, 0, 0) : Color.White;
|
||||||
|
|
||||||
|
// Update system warnings
|
||||||
|
UpdateWarningLabel(console.Flags);
|
||||||
|
|
||||||
|
// Reset nav map values
|
||||||
|
NavMap.TrackedCoordinates.Clear();
|
||||||
|
NavMap.TrackedEntities.Clear();
|
||||||
|
|
||||||
|
// Draw entities on the nav map
|
||||||
|
var entitiesOfInterest = new List<NetEntity>();
|
||||||
|
|
||||||
|
if (_focusEntity != null)
|
||||||
{
|
{
|
||||||
list.RemoveAt(list.Count - 1);
|
entitiesOfInterest.Add(_focusEntity.Value);
|
||||||
|
|
||||||
|
foreach (var entry in focusSources)
|
||||||
|
entitiesOfInterest.Add(entry.NetEntity);
|
||||||
|
|
||||||
|
foreach (var entry in focusLoads)
|
||||||
|
entitiesOfInterest.Add(entry.NetEntity);
|
||||||
}
|
}
|
||||||
while (list.Count < listVal.Length)
|
|
||||||
|
focusSources.Concat(focusLoads);
|
||||||
|
|
||||||
|
foreach ((var netEntity, var metaData) in console.PowerMonitoringDeviceMetaData)
|
||||||
{
|
{
|
||||||
list.AddItem("YOU SHOULD NEVER SEE THIS (REALLY!)", null, false);
|
if (NavMap.Visible)
|
||||||
|
AddTrackedEntityToNavMap(netEntity, metaData, entitiesOfInterest);
|
||||||
}
|
}
|
||||||
// Now overwrite the items properly...
|
|
||||||
for (var i = 0; i < listVal.Length; i++)
|
// Show monitor location
|
||||||
|
var mon = _entManager.GetNetEntity(_owner);
|
||||||
|
|
||||||
|
if (monitorCoords != null && mon != null)
|
||||||
{
|
{
|
||||||
var ent = listVal[i];
|
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
|
||||||
_prototypeManager.TryIndex(ent.IconEntityPrototypeId, out EntityPrototype? entityPrototype);
|
var blip = new NavMapBlip(monitorCoords.Value, texture, Color.Cyan, true, false);
|
||||||
IRsiStateLike? iconState = null;
|
NavMap.TrackedEntities[mon.Value] = blip;
|
||||||
if (entityPrototype != null)
|
|
||||||
iconState = _spriteSystem.GetPrototypeIcon(entityPrototype);
|
|
||||||
var icon = iconState?.GetFrame(RsiDirection.South, 0);
|
|
||||||
var item = list[i];
|
|
||||||
item.Text = $"{ent.NameLocalized} {Loc.GetString("power-monitoring-window-value", ("value", ent.Size))}";
|
|
||||||
item.Icon = icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update nav map
|
||||||
|
NavMap.ForceNavMapUpdate();
|
||||||
|
|
||||||
|
// If the entry group doesn't match the current tab, the data is out dated, do not use it
|
||||||
|
if (allEntries.Length > 0 && allEntries[0].Group != GetCurrentPowerMonitoringConsoleGroup())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Assign meta data to the console entries and sort them
|
||||||
|
allEntries = GetUpdatedPowerMonitoringConsoleEntries(allEntries, console);
|
||||||
|
focusSources = GetUpdatedPowerMonitoringConsoleEntries(focusSources, console);
|
||||||
|
focusLoads = GetUpdatedPowerMonitoringConsoleEntries(focusLoads, console);
|
||||||
|
|
||||||
|
// Get current console entry container
|
||||||
|
BoxContainer currentContainer = SourcesList;
|
||||||
|
switch (GetCurrentPowerMonitoringConsoleGroup())
|
||||||
|
{
|
||||||
|
case PowerMonitoringConsoleGroup.SMES:
|
||||||
|
currentContainer = SMESList; break;
|
||||||
|
case PowerMonitoringConsoleGroup.Substation:
|
||||||
|
currentContainer = SubstationList; break;
|
||||||
|
case PowerMonitoringConsoleGroup.APC:
|
||||||
|
currentContainer = ApcList; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear excess children from the container
|
||||||
|
while (currentContainer.ChildCount > allEntries.Length)
|
||||||
|
currentContainer.RemoveChild(currentContainer.GetChild(currentContainer.ChildCount - 1));
|
||||||
|
|
||||||
|
// Update the remaining children
|
||||||
|
for (var index = 0; index < allEntries.Length; index++)
|
||||||
|
{
|
||||||
|
var entry = allEntries[index];
|
||||||
|
|
||||||
|
if (entry.NetEntity == _focusEntity)
|
||||||
|
UpdateWindowConsoleEntry(currentContainer, index, entry, focusSources, focusLoads);
|
||||||
|
|
||||||
|
else
|
||||||
|
UpdateWindowConsoleEntry(currentContainer, index, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-scroll renable
|
||||||
|
if (_autoScrollAwaitsUpdate)
|
||||||
|
{
|
||||||
|
_autoScrollActive = true;
|
||||||
|
_autoScrollAwaitsUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTrackedEntityToNavMap(NetEntity netEntity, PowerMonitoringDeviceMetaData metaData, List<NetEntity> entitiesOfInterest)
|
||||||
|
{
|
||||||
|
if (!_groupBlips.TryGetValue(metaData.Group, out var data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var usedEntity = (metaData.CollectionMaster != null) ? metaData.CollectionMaster : netEntity;
|
||||||
|
var coords = _entManager.GetCoordinates(metaData.Coordinates);
|
||||||
|
var texture = data.Item1;
|
||||||
|
var color = data.Item2;
|
||||||
|
var blink = usedEntity == _focusEntity;
|
||||||
|
var modulator = Color.White;
|
||||||
|
|
||||||
|
if (_focusEntity != null && usedEntity != _focusEntity && !entitiesOfInterest.Contains(usedEntity.Value))
|
||||||
|
modulator = Color.DimGray;
|
||||||
|
|
||||||
|
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(texture), color * modulator, blink);
|
||||||
|
NavMap.TrackedEntities[netEntity] = blip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
|
||||||
|
{
|
||||||
|
if (netEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<PowerMonitoringConsoleComponent>(_owner, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!console.PowerMonitoringDeviceMetaData.TryGetValue(netEntity.Value, out var metaData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Switch entity for master, if applicable
|
||||||
|
// The master will always be in the same group as the entity
|
||||||
|
if (metaData.CollectionMaster != null)
|
||||||
|
netEntity = metaData.CollectionMaster;
|
||||||
|
|
||||||
|
_focusEntity = netEntity;
|
||||||
|
|
||||||
|
// Switch tabs
|
||||||
|
SwitchTabsBasedOnPowerMonitoringConsoleGroup(metaData.Group);
|
||||||
|
|
||||||
|
// Get the scroll position of the selected entity on the selected button the UI
|
||||||
|
ActivateAutoScrollToFocus();
|
||||||
|
|
||||||
|
// Send message to console that the focus has changed
|
||||||
|
SendPowerMonitoringConsoleMessageAction?.Invoke(_focusEntity, metaData.Group);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
AutoScrollToFocus();
|
||||||
|
|
||||||
|
// Warning sign pulse
|
||||||
|
var lit = _gameTiming.RealTime.TotalSeconds % BlinkFrequency > BlinkFrequency / 2f;
|
||||||
|
SystemWarningPanel.Modulate = lit ? Color.White : new Color(178, 178, 178);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PowerMonitoringConsoleEntry[] GetUpdatedPowerMonitoringConsoleEntries(PowerMonitoringConsoleEntry[] entries, PowerMonitoringConsoleComponent console)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
var entry = entries[i];
|
||||||
|
|
||||||
|
if (!console.PowerMonitoringDeviceMetaData.TryGetValue(entry.NetEntity, out var metaData))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
entries[i].MetaData = metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort all devices alphabetically by their entity name (not by power usage; otherwise their position on the UI will shift)
|
||||||
|
Array.Sort(entries, AlphabeticalSort);
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AlphabeticalSort(PowerMonitoringConsoleEntry x, PowerMonitoringConsoleEntry y)
|
||||||
|
{
|
||||||
|
if (x.MetaData?.EntityName == null)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (y.MetaData?.EntityName == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return x.MetaData.Value.EntityName.CompareTo(y.MetaData.Value.EntityName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
public struct PowerMonitoringConsoleTrackable
|
||||||
public sealed class PowerMonitoringConsoleBoundUserInterface : ComputerBoundUserInterface<PowerMonitoringWindow, PowerMonitoringConsoleBoundInterfaceState>
|
|
||||||
{
|
{
|
||||||
public PowerMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
public EntityUid EntityUid;
|
||||||
|
public PowerMonitoringConsoleGroup Group;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleTrackable(EntityUid uid, PowerMonitoringConsoleGroup group)
|
||||||
{
|
{
|
||||||
|
EntityUid = uid;
|
||||||
|
Group = group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Content.Server.Station.Components;
|
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Systems;
|
||||||
using Content.Server.Warps;
|
using Content.Server.Warps;
|
||||||
using Content.Shared.Pinpointer;
|
using Content.Shared.Pinpointer;
|
||||||
@@ -32,7 +31,7 @@ public sealed class NavMapSystem : SharedNavMapSystem
|
|||||||
SubscribeLocalEvent<StationGridAddedEvent>(OnStationInit);
|
SubscribeLocalEvent<StationGridAddedEvent>(OnStationInit);
|
||||||
SubscribeLocalEvent<NavMapComponent, ComponentStartup>(OnNavMapStartup);
|
SubscribeLocalEvent<NavMapComponent, ComponentStartup>(OnNavMapStartup);
|
||||||
SubscribeLocalEvent<NavMapComponent, ComponentGetState>(OnGetState);
|
SubscribeLocalEvent<NavMapComponent, ComponentGetState>(OnGetState);
|
||||||
SubscribeLocalEvent<NavMapComponent, GridSplitEvent>(OnNavMapSplit);
|
SubscribeLocalEvent<GridSplitEvent>(OnNavMapSplit);
|
||||||
|
|
||||||
SubscribeLocalEvent<NavMapBeaconComponent, ComponentStartup>(OnNavMapBeaconStartup);
|
SubscribeLocalEvent<NavMapBeaconComponent, ComponentStartup>(OnNavMapBeaconStartup);
|
||||||
SubscribeLocalEvent<NavMapBeaconComponent, AnchorStateChangedEvent>(OnNavMapBeaconAnchor);
|
SubscribeLocalEvent<NavMapBeaconComponent, AnchorStateChangedEvent>(OnNavMapBeaconAnchor);
|
||||||
@@ -84,7 +83,7 @@ public sealed class NavMapSystem : SharedNavMapSystem
|
|||||||
RefreshGrid(component, grid);
|
RefreshGrid(component, grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNavMapSplit(EntityUid uid, NavMapComponent component, ref GridSplitEvent args)
|
private void OnNavMapSplit(ref GridSplitEvent args)
|
||||||
{
|
{
|
||||||
var gridQuery = GetEntityQuery<MapGridComponent>();
|
var gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ public sealed class NavMapSystem : SharedNavMapSystem
|
|||||||
RefreshGrid(newComp, gridQuery.GetComponent(grid));
|
RefreshGrid(newComp, gridQuery.GetComponent(grid));
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshGrid(component, gridQuery.GetComponent(uid));
|
RefreshGrid(Comp<NavMapComponent>(args.Grid), gridQuery.GetComponent(args.Grid));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshGrid(NavMapComponent component, MapGridComponent grid)
|
private void RefreshGrid(NavMapComponent component, MapGridComponent grid)
|
||||||
@@ -201,7 +200,6 @@ public sealed class NavMapSystem : SharedNavMapSystem
|
|||||||
private void RefreshTile(MapGridComponent grid, NavMapComponent component, NavMapChunk chunk, Vector2i tile)
|
private void RefreshTile(MapGridComponent grid, NavMapComponent component, NavMapChunk chunk, Vector2i tile)
|
||||||
{
|
{
|
||||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||||
|
|
||||||
var existing = chunk.TileData;
|
var existing = chunk.TileData;
|
||||||
var flag = GetFlag(relative);
|
var flag = GetFlag(relative);
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,54 @@
|
|||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Tools;
|
using Content.Shared.Tools;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
|
|
||||||
namespace Content.Server.Power.Components
|
namespace Content.Server.Power.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows the attached entity to be destroyed by a cutting tool, dropping a piece of cable.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(CableSystem))]
|
||||||
|
public sealed partial class CableComponent : Component
|
||||||
{
|
{
|
||||||
|
[DataField("cableDroppedOnCutPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
|
public string CableDroppedOnCutPrototype = "CableHVStack1";
|
||||||
|
|
||||||
|
[DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
||||||
|
public string CuttingQuality = "Cutting";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows the attached entity to be destroyed by a cutting tool, dropping a piece of cable.
|
/// Checked by <see cref="CablePlacerComponent"/> to determine if there is
|
||||||
|
/// already a cable of a type on a tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[DataField("cableType")]
|
||||||
[Access(typeof(CableSystem))]
|
public CableType CableType = CableType.HighVoltage;
|
||||||
public sealed partial class CableComponent : Component
|
|
||||||
|
[DataField("cuttingDelay")]
|
||||||
|
public float CuttingDelay = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event to be raised when a cable is anchored / unanchored
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct CableAnchorStateChangedEvent
|
||||||
|
{
|
||||||
|
public readonly TransformComponent Transform;
|
||||||
|
public EntityUid Entity => Transform.Owner;
|
||||||
|
public bool Anchored => Transform.Anchored;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the entity is being detached to null-space
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Detaching;
|
||||||
|
|
||||||
|
public CableAnchorStateChangedEvent(TransformComponent transform, bool detaching = false)
|
||||||
{
|
{
|
||||||
[DataField("cableDroppedOnCutPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
Detaching = detaching;
|
||||||
public string CableDroppedOnCutPrototype = "CableHVStack1";
|
Transform = transform;
|
||||||
|
|
||||||
[DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
|
||||||
public string CuttingQuality = "Cutting";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checked by <see cref="CablePlacerComponent"/> to determine if there is
|
|
||||||
/// already a cable of a type on a tile.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("cableType")]
|
|
||||||
public CableType CableType = CableType.HighVoltage;
|
|
||||||
|
|
||||||
[DataField("cuttingDelay")]
|
|
||||||
public float CuttingDelay = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CableType
|
|
||||||
{
|
|
||||||
HighVoltage,
|
|
||||||
MediumVoltage,
|
|
||||||
Apc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
|
||||||
namespace Content.Server.Power.Components
|
namespace Content.Server.Power.Components
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Content.Server.Power.Components;
|
|
||||||
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class PowerMonitoringConsoleComponent : Component
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using Content.Server.NodeContainer;
|
||||||
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
|
||||||
|
namespace Content.Server.Power.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to flag any entities that should appear on a power monitoring console
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(PowerMonitoringConsoleSystem))]
|
||||||
|
public sealed partial class PowerMonitoringDeviceComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the node that this device draws its power from (see <see cref="NodeContainerComponent"/>)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("sourceNode"), ViewVariables]
|
||||||
|
public string SourceNode = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the node that this device distributes power to (see <see cref="NodeContainerComponent"/>)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("loadNode"), ViewVariables]
|
||||||
|
public string LoadNode = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Names of the nodes that this device can potentially distributes power to (see <see cref="NodeContainerComponent"/>)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("loadNodes"), ViewVariables]
|
||||||
|
public List<string>? LoadNodes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This entity will be grouped with entities that have the same collection name
|
||||||
|
/// </summary>
|
||||||
|
[DataField("collectionName"), ViewVariables]
|
||||||
|
public string CollectionName = string.Empty;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public BaseNodeGroup? NodeGroup = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the entity is/should be part of a collection
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCollectionMasterOrChild { get { return CollectionName != string.Empty; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the uid of the master that represents this entity
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Used when grouping multiple entities into a single power monitoring console entry
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables]
|
||||||
|
public EntityUid CollectionMaster;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if this entity represents a group of entities
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Used when grouping multiple entities into a single power monitoring console entry
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsCollectionMaster { get { return Owner == CollectionMaster; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of other entities that are to be represented by this entity
|
||||||
|
/// </summary>
|
||||||
|
/// /// <remarks>
|
||||||
|
/// Used when grouping multiple entities into a single power monitoring console entry
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<EntityUid, PowerMonitoringDeviceComponent> ChildDevices = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path to the .rsi folder
|
||||||
|
/// </summary>
|
||||||
|
[DataField("sprite"), ViewVariables]
|
||||||
|
public string SpritePath = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The .rsi state
|
||||||
|
/// </summary>
|
||||||
|
[DataField("state"), ViewVariables]
|
||||||
|
public string SpriteState = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines what power monitoring group this entity should belong to
|
||||||
|
/// </summary>
|
||||||
|
[DataField("group", required: true), ViewVariables]
|
||||||
|
public PowerMonitoringConsoleGroup Group;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using Content.Shared.Interaction;
|
|||||||
using Content.Shared.Tools;
|
using Content.Shared.Tools;
|
||||||
using Content.Shared.Tools.Components;
|
using Content.Shared.Tools.Components;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using System.Xml.Schema;
|
||||||
using CableCuttingFinishedEvent = Content.Shared.Tools.Systems.CableCuttingFinishedEvent;
|
using CableCuttingFinishedEvent = Content.Shared.Tools.Systems.CableCuttingFinishedEvent;
|
||||||
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
|
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ public sealed partial class CableSystem : EntitySystem
|
|||||||
[Dependency] private readonly StackSystem _stack = default!;
|
[Dependency] private readonly StackSystem _stack = default!;
|
||||||
[Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
|
[Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogs = default!;
|
[Dependency] private readonly IAdminLogManager _adminLogs = default!;
|
||||||
|
[Dependency] private readonly PowerMonitoringConsoleSystem _powerMonitoringSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -47,17 +49,24 @@ public sealed partial class CableSystem : EntitySystem
|
|||||||
if (args.Cancelled)
|
if (args.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var xform = Transform(uid);
|
||||||
|
var ev = new CableAnchorStateChangedEvent(xform);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
if (_electrocutionSystem.TryDoElectrifiedAct(uid, args.User))
|
if (_electrocutionSystem.TryDoElectrifiedAct(uid, args.User))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_adminLogs.Add(LogType.CableCut, LogImpact.Medium, $"The {ToPrettyString(uid)} at {Transform(uid).Coordinates} was cut by {ToPrettyString(args.User)}.");
|
_adminLogs.Add(LogType.CableCut, LogImpact.Medium, $"The {ToPrettyString(uid)} at {xform.Coordinates} was cut by {ToPrettyString(args.User)}.");
|
||||||
|
|
||||||
Spawn(cable.CableDroppedOnCutPrototype, Transform(uid).Coordinates);
|
Spawn(cable.CableDroppedOnCutPrototype, xform.Coordinates);
|
||||||
QueueDel(uid);
|
QueueDel(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAnchorChanged(EntityUid uid, CableComponent cable, ref AnchorStateChangedEvent args)
|
private void OnAnchorChanged(EntityUid uid, CableComponent cable, ref AnchorStateChangedEvent args)
|
||||||
{
|
{
|
||||||
|
var ev = new CableAnchorStateChangedEvent(args.Transform, args.Detaching);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
if (args.Anchored)
|
if (args.Anchored)
|
||||||
return; // huh? it wasn't anchored?
|
return; // huh? it wasn't anchored?
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Content.Server.StationEvents.Events;
|
using Content.Server.StationEvents.Events;
|
||||||
|
|
||||||
namespace Content.Server.StationEvents.Components;
|
namespace Content.Server.StationEvents.Components;
|
||||||
@@ -8,6 +8,7 @@ public sealed partial class PowerGridCheckRuleComponent : Component
|
|||||||
{
|
{
|
||||||
public CancellationTokenSource? AnnounceCancelToken;
|
public CancellationTokenSource? AnnounceCancelToken;
|
||||||
|
|
||||||
|
public EntityUid AffectedStation;
|
||||||
public readonly List<EntityUid> Powered = new();
|
public readonly List<EntityUid> Powered = new();
|
||||||
public readonly List<EntityUid> Unpowered = new();
|
public readonly List<EntityUid> Unpowered = new();
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ namespace Content.Server.StationEvents.Events
|
|||||||
if (!TryGetRandomStation(out var chosenStation))
|
if (!TryGetRandomStation(out var chosenStation))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
component.AffectedStation = chosenStation.Value;
|
||||||
|
|
||||||
var query = AllEntityQuery<ApcComponent, TransformComponent>();
|
var query = AllEntityQuery<ApcComponent, TransformComponent>();
|
||||||
while (query.MoveNext(out var apcUid ,out var apc, out var transform))
|
while (query.MoveNext(out var apcUid ,out var apc, out var transform))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Robust.Shared.GameStates;
|
|
||||||
|
|
||||||
namespace Content.Shared.Pinpointer;
|
namespace Content.Shared.Pinpointer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Shared.Pinpointer;
|
namespace Content.Shared.Pinpointer;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedPowerMonitoringConsoleSystem))]
|
||||||
|
public sealed partial class PowerMonitoringCableNetworksComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary of the all the nav map chunks that contain anchored power cables
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public Dictionary<Vector2i, PowerCableChunk> AllChunks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary of the all the nav map chunks that contain anchored power cables
|
||||||
|
/// that are directly connected to the console's current focus
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public Dictionary<Vector2i, PowerCableChunk> FocusChunks = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct PowerCableChunk
|
||||||
|
{
|
||||||
|
public readonly Vector2i Origin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bitmask dictionary for power cables, 1 for occupied and 0 for empty.
|
||||||
|
/// </summary>
|
||||||
|
public int[] PowerCableData;
|
||||||
|
|
||||||
|
public PowerCableChunk(Vector2i origin)
|
||||||
|
{
|
||||||
|
Origin = origin;
|
||||||
|
PowerCableData = new int[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Power
|
namespace Content.Shared.Power
|
||||||
{
|
{
|
||||||
@@ -23,4 +23,12 @@ namespace Content.Shared.Power
|
|||||||
WireCount,
|
WireCount,
|
||||||
CutWires
|
CutWires
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum CableType
|
||||||
|
{
|
||||||
|
HighVoltage,
|
||||||
|
MediumVoltage,
|
||||||
|
Apc,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,159 @@
|
|||||||
#nullable enable
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Power;
|
namespace Content.Shared.Power;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags an entity as being a power monitoring console
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedPowerMonitoringConsoleSystem), Other = AccessPermissions.ReadExecute)]
|
||||||
|
public sealed partial class PowerMonitoringConsoleComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The EntityUid of the device that is the console's current focus
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Not-networked - set by the console UI
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables]
|
||||||
|
public EntityUid? Focus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The group that the device that is the console's current focus belongs to
|
||||||
|
/// </summary>
|
||||||
|
/// /// <remarks>
|
||||||
|
/// Not-networked - set by the console UI
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables]
|
||||||
|
public PowerMonitoringConsoleGroup FocusGroup = PowerMonitoringConsoleGroup.Generator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of flags relating to currently active events of interest to the console.
|
||||||
|
/// E.g., power sinks, power net anomalies
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public PowerMonitoringFlags Flags = PowerMonitoringFlags.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary containing all the meta data for tracked power monitoring devices
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public Dictionary<NetEntity, PowerMonitoringDeviceMetaData> PowerMonitoringDeviceMetaData = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct PowerMonitoringDeviceMetaData
|
||||||
|
{
|
||||||
|
public string EntityName;
|
||||||
|
public NetCoordinates Coordinates;
|
||||||
|
public PowerMonitoringConsoleGroup Group;
|
||||||
|
public string SpritePath;
|
||||||
|
public string SpriteState;
|
||||||
|
public NetEntity? CollectionMaster;
|
||||||
|
|
||||||
|
public PowerMonitoringDeviceMetaData(string name, NetCoordinates coordinates, PowerMonitoringConsoleGroup group, string spritePath, string spriteState)
|
||||||
|
{
|
||||||
|
EntityName = name;
|
||||||
|
Coordinates = coordinates;
|
||||||
|
Group = group;
|
||||||
|
SpritePath = spritePath;
|
||||||
|
SpriteState = spriteState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data from by the server to the client for the power monitoring console UI
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class PowerMonitoringConsoleBoundInterfaceState : BoundUserInterfaceState
|
public sealed class PowerMonitoringConsoleBoundInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
public double TotalSources;
|
public double TotalSources;
|
||||||
|
public double TotalBatteryUsage;
|
||||||
public double TotalLoads;
|
public double TotalLoads;
|
||||||
public PowerMonitoringConsoleEntry[] Sources;
|
public PowerMonitoringConsoleEntry[] AllEntries;
|
||||||
public PowerMonitoringConsoleEntry[] Loads;
|
public PowerMonitoringConsoleEntry[] FocusSources;
|
||||||
public PowerMonitoringConsoleBoundInterfaceState(double totalSources, double totalLoads, PowerMonitoringConsoleEntry[] sources, PowerMonitoringConsoleEntry[] loads)
|
public PowerMonitoringConsoleEntry[] FocusLoads;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleBoundInterfaceState
|
||||||
|
(double totalSources,
|
||||||
|
double totalBatteryUsage,
|
||||||
|
double totalLoads,
|
||||||
|
PowerMonitoringConsoleEntry[] allEntries,
|
||||||
|
PowerMonitoringConsoleEntry[] focusSources,
|
||||||
|
PowerMonitoringConsoleEntry[] focusLoads)
|
||||||
{
|
{
|
||||||
TotalSources = totalSources;
|
TotalSources = totalSources;
|
||||||
|
TotalBatteryUsage = totalBatteryUsage;
|
||||||
TotalLoads = totalLoads;
|
TotalLoads = totalLoads;
|
||||||
Sources = sources;
|
AllEntries = allEntries;
|
||||||
Loads = loads;
|
FocusSources = focusSources;
|
||||||
|
FocusLoads = focusLoads;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all the data needed to update a single device on the power monitoring UI
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class PowerMonitoringConsoleEntry
|
public struct PowerMonitoringConsoleEntry
|
||||||
{
|
{
|
||||||
public string NameLocalized;
|
public NetEntity NetEntity;
|
||||||
public string IconEntityPrototypeId;
|
public PowerMonitoringConsoleGroup Group;
|
||||||
public double Size;
|
public double PowerValue;
|
||||||
public bool IsBattery;
|
|
||||||
public PowerMonitoringConsoleEntry(string nl, string ipi, double size, bool isBattery)
|
[NonSerialized] public PowerMonitoringDeviceMetaData? MetaData = null;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleEntry(NetEntity netEntity, PowerMonitoringConsoleGroup group, double powerValue = 0d)
|
||||||
{
|
{
|
||||||
NameLocalized = nl;
|
NetEntity = netEntity;
|
||||||
IconEntityPrototypeId = ipi;
|
Group = group;
|
||||||
Size = size;
|
PowerValue = powerValue;
|
||||||
IsBattery = isBattery;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the server to send updated power monitoring console data to the client for the single player session
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class PowerMonitoringConsoleMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public NetEntity? FocusDevice;
|
||||||
|
public PowerMonitoringConsoleGroup FocusGroup;
|
||||||
|
|
||||||
|
public PowerMonitoringConsoleMessage(NetEntity? focusDevice, PowerMonitoringConsoleGroup focusGroup)
|
||||||
|
{
|
||||||
|
FocusDevice = focusDevice;
|
||||||
|
FocusGroup = focusGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines how entities are grouped and color coded on the power monitor
|
||||||
|
/// </summary>
|
||||||
|
public enum PowerMonitoringConsoleGroup : byte
|
||||||
|
{
|
||||||
|
Generator,
|
||||||
|
SMES,
|
||||||
|
Substation,
|
||||||
|
APC,
|
||||||
|
Consumer,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum PowerMonitoringFlags : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
RoguePowerConsumer = 1,
|
||||||
|
PowerNetAbnormalities = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI key associated with the power monitoring console
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum PowerMonitoringConsoleUiKey
|
public enum PowerMonitoringConsoleUiKey
|
||||||
{
|
{
|
||||||
Key
|
Key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public abstract class SharedPowerMonitoringConsoleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
power-monitoring-window-title = Power Monitoring Console
|
power-monitoring-window-title = Power Monitoring Console
|
||||||
power-monitoring-window-tab-sources = Sources
|
|
||||||
power-monitoring-window-tab-loads = Loads
|
power-monitoring-window-label-sources = Sources
|
||||||
power-monitoring-window-total-sources = Total Sources:
|
power-monitoring-window-label-smes = SMES
|
||||||
power-monitoring-window-total-loads = Total Loads:
|
power-monitoring-window-label-substation = Substation
|
||||||
|
power-monitoring-window-label-apc = APC
|
||||||
|
power-monitoring-window-label-misc = Misc
|
||||||
|
|
||||||
|
power-monitoring-window-object-array = {$name} array [{$count}]
|
||||||
|
|
||||||
|
power-monitoring-window-station-name = [color=white][font size=14]{$stationName}[/font][/color]
|
||||||
|
power-monitoring-window-unknown-location = Unknown location
|
||||||
|
power-monitoring-window-total-sources = Total generator output
|
||||||
|
power-monitoring-window-total-battery-usage = Total battery usage
|
||||||
|
power-monitoring-window-total-loads = Total network loads
|
||||||
power-monitoring-window-value = { POWERWATTS($value) }
|
power-monitoring-window-value = { POWERWATTS($value) }
|
||||||
power-monitoring-window-show-inactive-consumers = Show Inactive Consumers
|
power-monitoring-window-show-inactive-consumers = Show Inactive Consumers
|
||||||
|
|
||||||
|
power-monitoring-window-show-cable-networks = Toggle cable networks
|
||||||
|
power-monitoring-window-show-hv-cable = High voltage
|
||||||
|
power-monitoring-window-show-mv-cable = Medium voltage
|
||||||
|
power-monitoring-window-show-lv-cable = Low voltage
|
||||||
|
|
||||||
|
power-monitoring-window-flavor-left = [user@nanotrasen] $run power_net_query
|
||||||
|
power-monitoring-window-flavor-right = v1.3
|
||||||
|
power-monitoring-window-rogue-power-consumer = [color=white][font size=14][bold]! WARNING - ROGUE POWER CONSUMING DEVICE DETECTED ![/bold][/font][/color]
|
||||||
|
power-monitoring-window-power-net-abnormalities = [color=white][font size=14][bold]CAUTION - ABNORMAL ACTIVITY IN POWER NET[/bold][/font][/color]
|
||||||
|
|||||||
@@ -239,6 +239,7 @@
|
|||||||
- type: Computer
|
- type: Computer
|
||||||
board: PowerComputerCircuitboard
|
board: PowerComputerCircuitboard
|
||||||
- type: PowerMonitoringConsole
|
- type: PowerMonitoringConsole
|
||||||
|
- type: PowerMonitoringCableNetworks
|
||||||
- type: NodeContainer
|
- type: NodeContainer
|
||||||
examinable: true
|
examinable: true
|
||||||
nodes:
|
nodes:
|
||||||
@@ -246,6 +247,7 @@
|
|||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
|
singleUser: true
|
||||||
key: enum.PowerMonitoringConsoleUiKey.Key
|
key: enum.PowerMonitoringConsoleUiKey.Key
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
|
|||||||
@@ -50,6 +50,12 @@
|
|||||||
input:
|
input:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: input
|
||||||
|
collectionName: radiationCollector
|
||||||
|
sprite: Structures/Power/Generation/Singularity/collector.rsi
|
||||||
|
state: static
|
||||||
- type: RadiationCollector
|
- type: RadiationCollector
|
||||||
chargeModifier: 7500
|
chargeModifier: 7500
|
||||||
radiationReactiveGases:
|
radiationReactiveGases:
|
||||||
|
|||||||
@@ -78,8 +78,11 @@
|
|||||||
input:
|
input:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
# - type: ApcPowerReceiver
|
- type: PowerMonitoringDevice
|
||||||
# - type: ExtensionCableReceiver
|
group: Generator
|
||||||
|
loadNode: input
|
||||||
|
sprite: Structures/Power/Generation/ame.rsi
|
||||||
|
state: static
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 0
|
supplyRate: 0
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
|
|||||||
@@ -40,6 +40,11 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/power.rsi
|
||||||
|
state: generator
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 3000
|
supplyRate: 3000
|
||||||
supplyRampRate: 500
|
supplyRampRate: 500
|
||||||
@@ -124,6 +129,11 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/Generation/wallmount_generator.rsi
|
||||||
|
state: static
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 3000
|
supplyRate: 3000
|
||||||
supplyRampRate: 500
|
supplyRampRate: 500
|
||||||
@@ -229,6 +239,9 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Power/Generation/rtg.rsi
|
sprite: Structures/Power/Generation/rtg.rsi
|
||||||
state: rtg
|
state: rtg
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
sprite: Structures/Power/Generation/rtg.rsi
|
||||||
|
state: rtg
|
||||||
- type: AmbientSound
|
- type: AmbientSound
|
||||||
range: 5
|
range: 5
|
||||||
sound:
|
sound:
|
||||||
@@ -272,6 +285,9 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: rtg_damaged
|
- state: rtg_damaged
|
||||||
- state: rtg_glow
|
- state: rtg_glow
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
sprite: Structures/Power/Generation/rtg.rsi
|
||||||
|
state: rtg_damaged
|
||||||
- type: RadiationSource # ideally only when opened.
|
- type: RadiationSource # ideally only when opened.
|
||||||
intensity: 2
|
intensity: 2
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
|
|||||||
@@ -170,7 +170,14 @@
|
|||||||
gasType: CarbonDioxide
|
gasType: CarbonDioxide
|
||||||
# 2 moles of gas for every sheet of plasma.
|
# 2 moles of gas for every sheet of plasma.
|
||||||
moleRatio: 2
|
moleRatio: 2
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNodes:
|
||||||
|
- output_hv
|
||||||
|
- output_mv
|
||||||
|
sprite: Structures/Power/Generation/portable_generator.rsi
|
||||||
|
state: portgen0
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: S.U.P.E.R.P.A.C.M.A.N.-type portable generator
|
name: S.U.P.E.R.P.A.C.M.A.N.-type portable generator
|
||||||
description: |-
|
description: |-
|
||||||
@@ -219,7 +226,14 @@
|
|||||||
- type: UpgradePowerSupplier
|
- type: UpgradePowerSupplier
|
||||||
powerSupplyMultiplier: 1.25
|
powerSupplyMultiplier: 1.25
|
||||||
scaling: Exponential
|
scaling: Exponential
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNodes:
|
||||||
|
- output_hv
|
||||||
|
- output_mv
|
||||||
|
sprite: Structures/Power/Generation/portable_generator.rsi
|
||||||
|
state: portgen1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: J.R.P.A.C.M.A.N.-type portable generator
|
name: J.R.P.A.C.M.A.N.-type portable generator
|
||||||
description: |-
|
description: |-
|
||||||
@@ -293,7 +307,12 @@
|
|||||||
nodes:
|
nodes:
|
||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: Apc
|
nodeGroupID: Apc
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/Generation/portable_generator.rsi
|
||||||
|
state: portgen3
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
# No ramping needed on this bugger.
|
# No ramping needed on this bugger.
|
||||||
voltage: Apc
|
voltage: Apc
|
||||||
|
|||||||
@@ -36,6 +36,12 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/Generation/solar_panel.rsi
|
||||||
|
state: static
|
||||||
|
collectionName: SolarPanel
|
||||||
- type: Anchorable
|
- type: Anchorable
|
||||||
- type: Pullable
|
- type: Pullable
|
||||||
- type: Electrified
|
- type: Electrified
|
||||||
|
|||||||
@@ -51,6 +51,11 @@
|
|||||||
teg:
|
teg:
|
||||||
!type:TegNodeGenerator
|
!type:TegNodeGenerator
|
||||||
nodeGroupID: Teg
|
nodeGroupID: Teg
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Generator
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/Generation/teg.rsi
|
||||||
|
state: static
|
||||||
- type: Rotatable
|
- type: Rotatable
|
||||||
|
|
||||||
# Note that only the TEG center is an AtmosDevice.
|
# Note that only the TEG center is an AtmosDevice.
|
||||||
|
|||||||
@@ -65,6 +65,13 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: Apc
|
nodeGroupID: Apc
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: APC
|
||||||
|
sourceNode: input
|
||||||
|
loadNode: output
|
||||||
|
collectionName: apc
|
||||||
|
sprite: Structures/Power/apc.rsi
|
||||||
|
state: static
|
||||||
- type: BatteryCharger
|
- type: BatteryCharger
|
||||||
voltage: Medium
|
voltage: Medium
|
||||||
- type: PowerProvider
|
- type: PowerProvider
|
||||||
|
|||||||
@@ -43,15 +43,22 @@
|
|||||||
examinable: true
|
examinable: true
|
||||||
nodes:
|
nodes:
|
||||||
input:
|
input:
|
||||||
!type:CableDeviceNode
|
|
||||||
nodeGroupID: HVPower
|
|
||||||
output:
|
|
||||||
!type:CableTerminalPortNode
|
!type:CableTerminalPortNode
|
||||||
nodeGroupID: HVPower
|
nodeGroupID: HVPower
|
||||||
- type: BatteryCharger
|
output:
|
||||||
|
!type:CableDeviceNode
|
||||||
|
nodeGroupID: HVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: SMES
|
||||||
|
sourceNode: input
|
||||||
|
loadNode: output
|
||||||
|
collectionName: smes
|
||||||
|
sprite: Structures/Power/smes.rsi
|
||||||
|
state: static
|
||||||
|
- type: BatteryDischarger
|
||||||
voltage: High
|
voltage: High
|
||||||
node: output
|
node: output
|
||||||
- type: BatteryDischarger
|
- type: BatteryCharger
|
||||||
voltage: High
|
voltage: High
|
||||||
node: input
|
node: input
|
||||||
- type: PowerNetworkBattery
|
- type: PowerNetworkBattery
|
||||||
|
|||||||
@@ -40,6 +40,13 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: MVPower
|
nodeGroupID: MVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Substation
|
||||||
|
sourceNode: input
|
||||||
|
loadNode: output
|
||||||
|
collectionName: substation
|
||||||
|
sprite: Structures/Power/substation.rsi
|
||||||
|
state: substation_static
|
||||||
- type: BatteryCharger
|
- type: BatteryCharger
|
||||||
voltage: High
|
voltage: High
|
||||||
- type: BatteryDischarger
|
- type: BatteryDischarger
|
||||||
@@ -166,6 +173,12 @@
|
|||||||
output:
|
output:
|
||||||
!type:CableDeviceNode
|
!type:CableDeviceNode
|
||||||
nodeGroupID: MVPower
|
nodeGroupID: MVPower
|
||||||
|
- type: PowerMonitoringDevice
|
||||||
|
group: Substation
|
||||||
|
sourceNode: input
|
||||||
|
loadNode: output
|
||||||
|
sprite: Structures/Power/substation.rsi
|
||||||
|
state: substation_wall_static
|
||||||
- type: BatteryCharger
|
- type: BatteryCharger
|
||||||
voltage: High
|
voltage: High
|
||||||
- type: BatteryDischarger
|
- type: BatteryDischarger
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/NavMap/beveled_hexagon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Resources/Textures/Interface/NavMap/beveled_square.png
Normal file
|
After Width: | Height: | Size: 904 B |
BIN
Resources/Textures/Interface/NavMap/beveled_triangle.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Resources/Textures/Interface/PowerMonitoring/load_arrow.png
Normal file
|
After Width: | Height: | Size: 826 B |
BIN
Resources/Textures/Interface/PowerMonitoring/source_arrow.png
Normal file
|
After Width: | Height: | Size: 774 B |
@@ -12,6 +12,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ca_on"
|
"name": "ca_on"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ca_active",
|
"name": "ca_active",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 664 B |
@@ -7,7 +7,7 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "shield_0"
|
"name": "shield_0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -86,6 +86,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "control"
|
"name": "control"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "control_critical",
|
"name": "control_critical",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 618 B |
@@ -1 +1,30 @@
|
|||||||
{"version":1,"license":"CC-BY-SA-3.0","copyright":"Taken from https://github.com/discordia-space/CEV-Eris/blob/d1e0161af146835f4fb79d21a6200caa9cc842d0/icons/obj/power.dmi and modified.","size":{"x":32,"y":32},"states":[{"name":"normal","select":[],"flags":{},"directions":8},{"name":"broken","select":[],"flags":{},"directions":1},{"name": "solar_assembly"},{"name": "solar_tracker"}]}
|
{
|
||||||
|
"version":1,
|
||||||
|
"license":"CC-BY-SA-3.0",
|
||||||
|
"copyright":"Taken from https://github.com/discordia-space/CEV-Eris/blob/d1e0161af146835f4fb79d21a6200caa9cc842d0/icons/obj/power.dmi and modified.",
|
||||||
|
"size":{"x":32,"y":32},
|
||||||
|
"states":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "normal",
|
||||||
|
"select": [],
|
||||||
|
"flags": {},
|
||||||
|
"directions": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "broken",
|
||||||
|
"select": [],
|
||||||
|
"flags": {},
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "solar_assembly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "solar_tracker"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 223 B |
@@ -7,9 +7,12 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "teg",
|
"name": "teg",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "teg-op1",
|
"name": "teg-op1",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -9,6 +9,9 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "panel"
|
"name": "panel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "on",
|
"name": "on",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 335 B |
@@ -9,6 +9,9 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "base"
|
"name": "base"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "broken"
|
"name": "broken"
|
||||||
|
|||||||
BIN
Resources/Textures/Structures/Power/apc.rsi/static.png
Normal file
|
After Width: | Height: | Size: 384 B |
@@ -9,6 +9,9 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "smes"
|
"name": "smes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "static"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "smes-open"
|
"name": "smes-open"
|
||||||
|
|||||||
BIN
Resources/Textures/Structures/Power/smes.rsi/static.png
Normal file
|
After Width: | Height: | Size: 884 B |
@@ -10,9 +10,15 @@
|
|||||||
{
|
{
|
||||||
"name": "substation"
|
"name": "substation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "substation_static"
|
||||||
|
},
|
||||||
|
{
|
||||||
"name": "substation_wall"
|
"name": "substation_wall"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "substation_wall_static"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "full"
|
"name": "full"
|
||||||
},
|
},
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 444 B |