Port Supermatter Updates From Impstation (#1719)

# Description

Yea it turns out ImpStation made a shitton of updates to Supermatter
that were never upstreamed, including making a fancy new Supermatter
Monitoring Console, which needs to be mapped. Technically this is a
DeltaV PR, but out of pure spite I ported the entirety of this
completely by hand.

<details><summary><h1>Media</h1></summary>
<p>

![image](https://github.com/user-attachments/assets/da665b53-ad0a-4661-bb31-8e9b3bd01b27)

</p>
</details>

# Changelog

🆑
- add: Added Supermatter Monitoring Console.

(cherry picked from commit 541f1695d0f9e03afb26f847f3e8fd1462c5ed14)
This commit is contained in:
VMSolidus
2025-02-17 18:06:11 -05:00
committed by Spatison
parent ce00796560
commit 2a7f4e070c
132 changed files with 2564 additions and 341 deletions

View File

@@ -0,0 +1,40 @@
using Content.Shared._EE.Supermatter.Components;
namespace Content.Client._EE.Supermatter.Consoles;
public sealed class SupermatterConsoleBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private SupermatterConsoleWindow? _menu;
public SupermatterConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
protected override void Open()
{
_menu = new SupermatterConsoleWindow(this, Owner);
_menu.OpenCentered();
_menu.OnClose += Close;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (SupermatterConsoleBoundInterfaceState)state;
_menu?.UpdateUI(castState.Supermatters, castState.FocusData);
}
public void SendFocusChangeMessage(NetEntity? netEntity)
{
SendMessage(new SupermatterConsoleFocusChangeMessage(netEntity));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
}

View File

@@ -0,0 +1,34 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'supermatter-console-window-title'}"
Resizable="True"
SetSize="700 400"
MinSize="700 400">
<BoxContainer Orientation="Vertical">
<!-- Main display -->
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<!-- Supermatter list -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="10 0 10 10">
<!-- Supermatters (entries added by C# code) -->
<BoxContainer VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
<ScrollContainer HorizontalExpand="True">
<BoxContainer Name="SupermattersTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10" />
</ScrollContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Footer -->
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc 'supermatter-console-window-flavor-left'}" StyleClasses="WindowFooterText" />
<Label Text="{Loc 'supermatter-console-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>
</controls:FancyWindow>

View File

@@ -0,0 +1,258 @@
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared._EE.Supermatter.Components;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Pinpointer;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Client._EE.Supermatter.Consoles;
[GenerateTypedNameReferences]
public sealed partial class SupermatterConsoleWindow : FancyWindow
{
private readonly IEntityManager _entManager;
private readonly SpriteSystem _spriteSystem;
private readonly SharedNavMapSystem _navMapSystem;
private EntityUid? _owner;
private NetEntity? _trackedEntity;
private SupermatterConsoleEntry[]? _supermatters = null;
private IEnumerable<SupermatterConsoleEntry>? _activeAlerts = null;
public event Action<NetEntity?>? SendFocusChangeMessageAction;
private bool _autoScrollActive = false;
private bool _autoScrollAwaitsUpdate = false;
// Colors
private Color _wallColor = new Color(64, 64, 64);
private Color _tileColor = new Color(28, 28, 28);
private Color _monitorBlipColor = Color.Cyan;
private Color _untrackedEntColor = Color.DimGray;
private Color _regionBaseColor = new Color(154, 154, 154);
private Color _inactiveColor = StyleNano.DisabledFore;
private Color _statusTextColor = StyleNano.GoodGreenFore;
private Color _goodColor = Color.LimeGreen;
private Color _warningColor = new Color(255, 182, 72);
private Color _dangerColor = new Color(255, 67, 67);
public SupermatterConsoleWindow(SupermatterConsoleBoundUserInterface userInterface, EntityUid? owner)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_spriteSystem = _entManager.System<SpriteSystem>();
_navMapSystem = _entManager.System<SharedNavMapSystem>();
// Pass the owner to nav map
_owner = owner;
// Set supermatter monitoring message action
SendFocusChangeMessageAction += userInterface.SendFocusChangeMessage;
}
public void UpdateUI(SupermatterConsoleEntry[] supermatters, SupermatterFocusData? focusData)
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<SupermatterConsoleComponent>(_owner.Value, out var console))
return;
if (_trackedEntity != focusData?.NetEntity)
{
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
focusData = null;
}
// Retain supermatter data for use inbetween updates
_supermatters = supermatters;
// Clear excess children from the tables
var supermattersCount = _supermatters.Count();
while (SupermattersTable.ChildCount > supermattersCount)
SupermattersTable.RemoveChild(SupermattersTable.GetChild(SupermattersTable.ChildCount - 1));
// Update all entries in each table
for (int index = 0; index < _supermatters.Count(); index++)
{
var entry = _supermatters.ElementAt(index);
UpdateUIEntry(entry, index, SupermattersTable, console, focusData);
}
// If no alerts are active, display a message
if (supermattersCount == 0)
{
var label = new RichTextLabel()
{
HorizontalExpand = true,
VerticalExpand = true,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
};
label.SetMarkup(Loc.GetString("supermatter-console-window-no-supermatters"));
SupermattersTable.AddChild(label);
}
// Auto-scroll re-enable
if (_autoScrollAwaitsUpdate)
{
_autoScrollActive = true;
_autoScrollAwaitsUpdate = false;
}
}
private void UpdateUIEntry(SupermatterConsoleEntry entry, int index, Control table, SupermatterConsoleComponent console, SupermatterFocusData? focusData = null)
{
// Make new UI entry if required
if (index >= table.ChildCount)
{
var newEntryContainer = new SupermatterEntryContainer(entry.NetEntity);
// On click
newEntryContainer.FocusButton.OnButtonUp += args =>
{
if (_trackedEntity == newEntryContainer.NetEntity)
{
_trackedEntity = null;
}
else
{
_trackedEntity = newEntryContainer.NetEntity;
}
// Send message to console that the focus has changed
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
};
// Add the entry to the current table
table.AddChild(newEntryContainer);
}
// Update values and UI elements
var tableChild = table.GetChild(index);
if (tableChild is not SupermatterEntryContainer)
{
table.RemoveChild(tableChild);
UpdateUIEntry(entry, index, table, console, focusData);
return;
}
var entryContainer = (SupermatterEntryContainer)tableChild;
entryContainer.UpdateEntry(entry, entry.NetEntity == _trackedEntity, focusData);
}
protected override void FrameUpdate(FrameEventArgs args)
{
AutoScrollToFocus();
}
private void ActivateAutoScrollToFocus()
{
_autoScrollActive = false;
_autoScrollAwaitsUpdate = true;
}
private void AutoScrollToFocus()
{
if (!_autoScrollActive)
return;
var scroll = SupermattersTable.Parent 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 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 bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
{
nextScrollPosition = null;
var scroll = SupermattersTable.Parent 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 SupermatterEntryContainer)
continue;
if (((SupermatterEntryContainer)control).NetEntity == _trackedEntity)
return true;
nextScrollPosition += control.Height;
}
// Failed to find control
nextScrollPosition = null;
return false;
}
private SupermatterStatusType GetStatus(NetEntity netEntity)
{
var status = _supermatters?.FirstOrNull(x => x.NetEntity == netEntity)?.EntityStatus;
if (status == null)
return SupermatterStatusType.Error;
return status.Value;
}
}

View File

@@ -0,0 +1,107 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
<Button Name="FocusButton" VerticalExpand="True" HorizontalExpand="True" StyleClasses="OpenLeft" Access="Public">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
<!-- Supermatter sprite -->
<TextureRect Name="SupermatterSprite" SetWidth="32" SetHeight="48" TexturePath="/Textures/_EE/Interface/Supermatter/supermatter.png" Margin="6 2" />
<!-- Supermatter name -->
<Label Name="SupermatterNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Left" Margin="5 0" />
<!-- Supermatter status -->
<Label Name="SupermatterStatusLabel" Text="{Loc 'supermatter-console-window-error-status'}" HorizontalExpand="True" HorizontalAlignment="Right" Margin="5 0" />
</BoxContainer>
</Button>
<!-- Panel that appears on selecting the device -->
<PanelContainer Name="FocusContainer" HorizontalExpand="True" Margin="1 -1 1 0" ReservesSpace="False" Visible="False" Access="Public">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
</PanelContainer.PanelOverride>
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal" Margin="6 3">
<!-- Engine details -->
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" Margin="0 0 6 0">
<!-- Integrity display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="IntegrityLabel" Text="{Loc 'supermatter-console-window-label-integrity'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="IntegrityBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="IntegrityBar" MinValue="0" MaxValue="100" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="IntegrityBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Internal energy display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="PowerLabel" Text="{Loc 'supermatter-console-window-label-power'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="PowerBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="PowerBar" MinValue="0" MaxValue="5000" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="PowerBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Radiation display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="RadiationLabel" Text="{Loc 'supermatter-console-window-label-radiation'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="RadiationBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="RadiationBar" MinValue="0" MaxValue="30" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="RadiationBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Absorbed moles display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="MolesLabel" Text="{Loc 'supermatter-console-window-label-moles'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="MolesBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="MolesBar" MinValue="0" MaxValue="100" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="MolesBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Temperature display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="TemperatureLabel" Text="{Loc 'supermatter-console-window-label-temperature'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="TemperatureBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="TemperatureBar" MinValue="0" MaxValue="1000" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="TemperatureBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Temperature limit display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="TemperatureLimitLabel" Text="{Loc 'supermatter-console-window-label-temperature-limit'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<Label Name="TemperatureLimitBarLabel" HorizontalAlignment="Left" SetWidth="150" />
</BoxContainer>
<!-- Waste multiplier display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="WasteLabel" Text="{Loc 'supermatter-console-window-label-waste'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="WasteBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="WasteBar" MinValue="0" MaxValue="10" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="WasteBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
<!-- Absorption ratio display -->
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="AbsorptionLabel" Text="{Loc 'supermatter-console-window-label-absorption'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<Label Name="AbsorptionBarLabel" HorizontalAlignment="Left" SetWidth="150" />
</BoxContainer>
</BoxContainer>
<!-- Gas details (entries added by C# code) -->
<BoxContainer Name="GasTable" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" Margin="6 0 0 0" />
</BoxContainer>
</PanelContainer>
</BoxContainer>

View File

@@ -0,0 +1,285 @@
using Content.Client.Atmos.EntitySystems;
using Content.Client.Stylesheets;
using Content.Shared._EE.Supermatter.Components;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using System.Linq;
namespace Content.Client._EE.Supermatter.Consoles;
[GenerateTypedNameReferences]
public sealed partial class SupermatterEntryContainer : BoxContainer
{
public NetEntity NetEntity;
private readonly IEntityManager _entManager;
private readonly IResourceCache _cache;
private Dictionary<SupermatterStatusType, string> _statusStrings = new()
{
[SupermatterStatusType.Inactive] = "supermatter-console-window-inactive-status",
[SupermatterStatusType.Normal] = "supermatter-console-window-normal-status",
[SupermatterStatusType.Caution] = "supermatter-console-window-caution-status",
[SupermatterStatusType.Warning] = "supermatter-console-window-warning-status",
[SupermatterStatusType.Danger] = "supermatter-console-window-danger-status",
[SupermatterStatusType.Emergency] = "supermatter-console-window-emergency-status",
[SupermatterStatusType.Delaminating] = "supermatter-console-window-delaminating-status"
};
public SupermatterEntryContainer(NetEntity uid)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>();
NetEntity = uid;
// Load fonts
var headerFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11);
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
var monoFont = new VectorFont(_cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSansMono-Regular.ttf"), 10);
// Set fonts
SupermatterNameLabel.FontOverride = headerFont;
SupermatterStatusLabel.FontOverride = normalFont;
IntegrityLabel.FontOverride = normalFont;
PowerLabel.FontOverride = normalFont;
RadiationLabel.FontOverride = normalFont;
MolesLabel.FontOverride = normalFont;
TemperatureLabel.FontOverride = normalFont;
TemperatureLimitLabel.FontOverride = normalFont;
WasteLabel.FontOverride = normalFont;
AbsorptionLabel.FontOverride = normalFont;
IntegrityBarLabel.FontOverride = monoFont;
PowerBarLabel.FontOverride = monoFont;
RadiationBarLabel.FontOverride = monoFont;
MolesBarLabel.FontOverride = monoFont;
TemperatureBarLabel.FontOverride = monoFont;
TemperatureLimitBarLabel.FontOverride = monoFont;
WasteBarLabel.FontOverride = monoFont;
AbsorptionBarLabel.FontOverride = monoFont;
}
public void UpdateEntry(SupermatterConsoleEntry entry, bool isFocus, SupermatterFocusData? focusData = null)
{
NetEntity = entry.NetEntity;
// Load fonts
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
// Update supermatter name
SupermatterNameLabel.Text = Loc.GetString("supermatter-console-window-label-sm", ("name", entry.EntityName));
// Update supermatter status
SupermatterStatusLabel.Text = Loc.GetString(GetStatusLabel(entry.EntityStatus));
SupermatterStatusLabel.FontColorOverride = GetStatusColor(entry.EntityStatus);
// Focus updates
FocusContainer.Visible = isFocus;
if (isFocus)
{
if (focusData != null)
{
var red = StyleNano.DangerousRedFore;
var orange = StyleNano.ConcerningOrangeFore;
var green = StyleNano.GoodGreenFore;
var turqoise = Color.FromHex("#009893");
// TODO: please don't define this dictionary every update you animal
var engineDictionary = new Dictionary<string, (Label label, ProgressBar bar, PanelContainer border, float value, float leftSize, float rightSize, Color leftColor, Color middleColor, Color rightColor)>()
{
{ "integrity", (IntegrityBarLabel, IntegrityBar, IntegrityBarBorder, focusData.Value.Integrity, 0.9f, 0.1f, red, orange, green) },
{ "power", (PowerBarLabel, PowerBar, PowerBarBorder, focusData.Value.Power, 0.9f, 0.1f, green, orange, red ) },
{ "radiation", (RadiationBarLabel, RadiationBar, RadiationBarBorder, focusData.Value.Radiation, 0.1f, 0.9f, green, orange, red ) },
{ "moles", (MolesBarLabel, MolesBar, MolesBarBorder, focusData.Value.AbsorbedMoles, 0.5f, 0.5f, green, orange, red ) },
{ "temperature", (TemperatureBarLabel, TemperatureBar, TemperatureBarBorder, focusData.Value.Temperature, 0.5f, 0.5f, turqoise, green, red ) },
{ "waste", (WasteBarLabel, WasteBar, WasteBarBorder, focusData.Value.WasteMultiplier, 0.5f, 0.5f, green, orange, red ) }
};
// Special cases
var powerBar = engineDictionary["power"];
var powerPrefix = powerBar.value switch { >= 1000 => "G", >= 1 => "M", _ => "" };
var powerMultiplier = powerBar.value switch { >= 1000 => 0.001, >= 1 => 1, _ => 1000 };
powerBar.label.Text = Loc.GetString("supermatter-console-window-label-power-bar", ("power", (powerBar.value * powerMultiplier).ToString("0.000")), ("prefix", powerPrefix));
var temperatureLimit = focusData.Value.TemperatureLimit;
TemperatureBar.MaxValue = temperatureLimit;
TemperatureLimitBarLabel.Text = Loc.GetString("supermatter-console-window-label-temperature-bar", ("temperature", temperatureLimit.ToString("0.00")));
var absorptionRatio = focusData.Value.AbsorptionRatio;
AbsorptionBarLabel.Text = Loc.GetString("supermatter-console-window-label-absorption-bar", ("absorption", absorptionRatio.ToString("0")));
// Update engine bars
foreach (var bar in engineDictionary)
{
var current = bar.Value;
UpdateEngineBar(current.bar, current.border, current.value, current.leftSize, current.rightSize, current.leftColor, current.middleColor, current.rightColor);
if (bar.Key == "power")
continue;
current.label.Text = Loc.GetString("supermatter-console-window-label-" + bar.Key + "-bar", (bar.Key, current.value.ToString("0.00")));
}
// Update gas bars
var atmosphereSystem = _entManager.System<AtmosphereSystem>();
var gases = atmosphereSystem.Gases.OrderByDescending(gas => GetStoredGas(gas, focusData));
var index = 0;
foreach (var gas in gases)
{
var name = gas.Name;
var color = Color.FromHex("#" + gas.Color);
var value = GetStoredGas(gas, focusData) / focusData.Value.AbsorbedMoles * 100;
UpdateGasBar(index, GasTable, name, color, value);
index++;
}
}
}
}
private string GetStatusLabel(SupermatterStatusType status)
{
switch (status)
{
case SupermatterStatusType.Inactive:
return "supermatter-console-window-inactive-status";
case SupermatterStatusType.Normal:
return "supermatter-console-window-normal-status";
case SupermatterStatusType.Caution:
return "supermatter-console-window-caution-status";
case SupermatterStatusType.Warning:
return "supermatter-console-window-warning-status";
case SupermatterStatusType.Danger:
return "supermatter-console-window-danger-status";
case SupermatterStatusType.Emergency:
return "supermatter-console-window-emergency-status";
case SupermatterStatusType.Delaminating:
return "supermatter-console-window-delaminating-status";
}
return "supermatter-console-window-error-status";
}
private Color GetStatusColor(SupermatterStatusType status)
{
switch (status)
{
case SupermatterStatusType.Inactive:
return Color.DarkGray;
case SupermatterStatusType.Normal:
return StyleNano.GoodGreenFore;
case SupermatterStatusType.Caution:
case SupermatterStatusType.Warning:
return StyleNano.ConcerningOrangeFore;
case SupermatterStatusType.Danger:
case SupermatterStatusType.Emergency:
case SupermatterStatusType.Delaminating:
return StyleNano.DangerousRedFore;
}
return StyleNano.DisabledFore;
}
private float GetStoredGas(GasPrototype gas, SupermatterFocusData? focusData)
{
var id = int.Parse(gas.ID);
if (focusData == null)
return 0;
return focusData.Value.GasStorage[(Gas)id];
}
private void UpdateEngineBar(ProgressBar bar, PanelContainer border, float value, float leftSize, float rightSize, Color leftColor, Color middleColor, Color rightColor)
{
var clamped = Math.Clamp(value, bar.MinValue, bar.MaxValue);
var normalized = clamped / bar.MaxValue;
var leftHsv = Color.ToHsv(leftColor);
var middleHsv = Color.ToHsv(middleColor);
var rightHsv = Color.ToHsv(rightColor);
// Ensure leftSize and rightSize add up to 1.0 or the transition won't be smooth
var minColor = new Vector4(0, 0, 0, 0);
var maxColor = new Vector4(1, 1, 1, 1);
Color finalColor;
if (normalized <= leftSize)
{
normalized /= leftSize; // Adjust range to 0.0 to 1.0
var calcColor = Vector4.Lerp(leftHsv, middleHsv, normalized);
var clampedColor = Vector4.Clamp(calcColor, minColor, maxColor);
finalColor = Color.FromHsv(clampedColor);
}
else
{
normalized = (normalized - leftSize) / rightSize; // Adjust range to 0.0 to 1.0
var calcColor = Vector4.Lerp(middleHsv, rightHsv, normalized);
var clampedColor = Vector4.Clamp(calcColor, minColor, maxColor);
finalColor = Color.FromHsv(clampedColor);
}
// Check if null first to avoid repeatedly creating this.
bar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
border.PanelOverride ??= new StyleBoxFlat();
var barOverride = (StyleBoxFlat)bar.ForegroundStyleBoxOverride;
barOverride.BackgroundColor = finalColor;
var panelOverride = (StyleBoxFlat)border.PanelOverride;
panelOverride.BackgroundColor = finalColor;
bar.Value = clamped;
}
private void UpdateGasBar(int index, Control table, string name, Color color, float value)
{
// Make new UI entry if required
if (index >= table.ChildCount)
{
var newEntryContainer = new SupermatterGasBarContainer();
// Add the entry to the current table
table.AddChild(newEntryContainer);
}
// Update values and UI elements
var tableChild = table.GetChild(index);
if (tableChild is not SupermatterGasBarContainer)
{
table.RemoveChild(tableChild);
UpdateGasBar(index, table, name, color, value);
return;
}
var entryContainer = (SupermatterGasBarContainer)tableChild;
entryContainer.UpdateEntry(name, color, value);
}
}

View File

@@ -0,0 +1,16 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical" HorizontalExpand ="True">
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="0 1">
<Label Name="GasLabel" Text="{Loc 'supermatter-console-window-label-gas'}" HorizontalExpand="True" HorizontalAlignment="Left" />
<PanelContainer Name="GasBarBorder" SetWidth="150" SetHeight="24">
<ProgressBar Name="GasBar" MinValue="0" MaxValue="100" HorizontalExpand="True" VerticalExpand="True" Margin="1">
<Label Name="GasBarLabel" HorizontalAlignment="Right" Margin="0 0 4 0" />
</ProgressBar>
</PanelContainer>
</BoxContainer>
</BoxContainer>

View File

@@ -0,0 +1,47 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._EE.Supermatter.Consoles;
[GenerateTypedNameReferences]
public sealed partial class SupermatterGasBarContainer : BoxContainer
{
private readonly IEntityManager _entManager;
private readonly IResourceCache _cache;
public SupermatterGasBarContainer()
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>();
// Load fonts
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
var monoFont = new VectorFont(_cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSansMono-Regular.ttf"), 10);
// Set fonts
GasLabel.FontOverride = normalFont;
GasBarLabel.FontOverride = monoFont;
}
public void UpdateEntry(string name, Color color, float value)
{
GasBar.Value = value;
GasLabel.Text = Loc.GetString(name) + ":";
GasBarLabel.Text = Loc.GetString("supermatter-console-window-label-gas-bar", ("gas", value.ToString("0.00")));
// Check if null first to avoid repeatedly creating this.
GasBar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
GasBarBorder.PanelOverride ??= new StyleBoxFlat();
var barOverride = (StyleBoxFlat)GasBar.ForegroundStyleBoxOverride;
barOverride.BackgroundColor = color;
var borderOverride = (StyleBoxFlat)GasBarBorder.PanelOverride;
borderOverride.BackgroundColor = color;
}
}

View File

@@ -1,10 +1,9 @@
using System.Numerics;
using System.Numerics;
using Content.Server.Beam.Components;
using Content.Shared.Beam;
using Content.Shared.Beam.Components;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Physics;
@@ -83,6 +82,11 @@ public sealed class BeamSystem : SharedBeamSystem
if (!TryComp<PhysicsComponent>(ent, out var physics) || !TryComp<BeamComponent>(ent, out var beam))
return;
// imp - start non-randomized lightning sprites
if (!beam.AllowSpriteOverwrite)
bodyState = null;
// imp - end non-randomized lightning sprites
FixturesComponent? manager = null;
_fixture.TryCreateFixture(
ent,
@@ -146,21 +150,8 @@ public sealed class BeamSystem : SharedBeamSystem
if (Deleted(user) || Deleted(target))
return;
var userMapPos = _transform.GetMapCoordinates(user);
var targetMapPos = _transform.GetMapCoordinates(target);
//The distance between the target and the user.
var calculatedDistance = targetMapPos.Position - userMapPos.Position;
var userAngle = calculatedDistance.ToWorldAngle();
if (userMapPos.MapId != targetMapPos.MapId)
return;
//Where the start of the beam will spawn
var beamStartPos = userMapPos.Offset(calculatedDistance.Normalized());
//Don't divide by zero
if (calculatedDistance.Length() == 0)
// imp - Beam creation was changed to a coordinate-to-coordinate method to allow for more flexibility.
if (!TryCreateBeam(_transform.GetMapCoordinates(user), _transform.GetMapCoordinates(target), bodyPrototype, bodyState, shader, controller))
return;
if (controller != null && TryComp<BeamComponent>(controller, out var controllerBeamComp))
@@ -169,11 +160,95 @@ public sealed class BeamSystem : SharedBeamSystem
controllerBeamComp.HitTargets.Add(target);
}
var distanceCorrection = calculatedDistance - calculatedDistance.Normalized();
CreateBeam(bodyPrototype, userAngle, calculatedDistance, beamStartPos, distanceCorrection, controller, bodyState, shader);
var ev = new CreateBeamSuccessEvent(user, target);
RaiseLocalEvent(user, ev);
}
/// <summary>
/// Called where you want an entity to create a beam from one set of coordinates to another.
/// Tries to create the beam and does calculations like the distance, angle, and offset.
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="coordinates">The coordinates that the beam is being fired from</param>
/// <param name="targetCoordinates">The coordinates that are being targeted</param>
/// <param name="bodyPrototype">The prototype spawned when this beam is created</param>
/// <param name="bodyState">Optional sprite state for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="shader">Optional shader for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="controller"></param>
public bool TryCreateBeam(MapCoordinates coordinates, MapCoordinates targetCoordinates, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null)
{
//The distance between the target and the user.
var calculatedDistance = targetCoordinates.Position - coordinates.Position;
var userAngle = calculatedDistance.ToWorldAngle();
if (coordinates.MapId != targetCoordinates.MapId)
return false;
//Where the start of the beam will spawn
var beamStartPos = coordinates.Offset(calculatedDistance.Normalized());
//Don't divide by zero
if (calculatedDistance.Length() == 0)
return false;
var distanceCorrection = calculatedDistance - calculatedDistance.Normalized();
CreateBeam(bodyPrototype, userAngle, calculatedDistance, beamStartPos, distanceCorrection, controller, bodyState, shader);
return true;
}
/// <summary>
/// Called where you want an entity to create a beam from a set of coordinates to an entity.
/// Tries to create the beam and does calculations like the distance, angle, and offset.
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="coordinates">The coordinates that the beam is being fired from</param>
/// <param name="target">The entity that's being targeted by the user</param>
/// <param name="bodyPrototype">The prototype spawned when this beam is created</param>
/// <param name="bodyState">Optional sprite state for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="shader">Optional shader for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="controller"></param>
public void TryCreateBeam(MapCoordinates coordinates, EntityUid target, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null)
{
if (Deleted(target))
return;
if (TryCreateBeam(coordinates, _transform.GetMapCoordinates(target), bodyPrototype, bodyState, shader, controller))
{
if (controller != null && TryComp<BeamComponent>(controller, out var controllerBeamComp))
controllerBeamComp.HitTargets.Add(target);
}
}
/// <summary>
/// Called where you want an entity to create a beam from an entity to a set of coordinates.
/// Tries to create the beam and does calculations like the distance, angle, and offset.
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="user">The entity that's firing off the beam</param>
/// <param name="targetCoordinates">The coordinates that are being targeted</param>
/// <param name="bodyPrototype">The prototype spawned when this beam is created</param>
/// <param name="bodyState">Optional sprite state for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="shader">Optional shader for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="controller"></param>
public void TryCreateBeam(EntityUid user, MapCoordinates targetCoordinates, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null)
{
if (Deleted(user))
return;
if (TryCreateBeam(_transform.GetMapCoordinates(user), targetCoordinates, bodyPrototype, bodyState, shader, controller))
{
if (controller != null && TryComp<BeamComponent>(controller, out var controllerBeamComp))
controllerBeamComp.HitTargets.Add(user);
var ev = new CreateBeamSuccessEvent(user, targetCoordinates);
RaiseLocalEvent(user, ev);
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.Beam.Components;
using Content.Server.Lightning.Components;
using Content.Shared.Lightning;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server.Lightning;
@@ -21,7 +22,7 @@ public sealed class LightningSystem : SharedLightningSystem
[Dependency] private readonly BeamSystem _beam = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TransformSystem _transform = default!;
public override void Initialize()
{
@@ -59,6 +60,134 @@ public sealed class LightningSystem : SharedLightningSystem
}
}
/// <summary>
/// Fires lightning from user to coordinates
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="user">Where the lightning fires from</param>
/// <param name="targetCoordinates">Where the lightning fires to</param>
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
/// <param name="triggerLightningEvents">if the lightnings being fired should trigger lightning events.</param>
public void ShootLightning(EntityUid user, MapCoordinates targetCoordinates, string lightningPrototype = "Lightning", bool triggerLightningEvents = true)
{
var spriteState = LightningRandomizer();
_beam.TryCreateBeam(user, targetCoordinates, lightningPrototype, spriteState);
}
/// <summary>
/// Fires lightning from coordinates to target
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="coordinates">Where the lightning fires from</param>
/// <param name="target">Where the lightning fires to</param>
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
/// <param name="triggerLightningEvents">if the lightnings being fired should trigger lightning events.</param>
public void ShootLightning(MapCoordinates coordinates, EntityUid target, string lightningPrototype = "Lightning", bool triggerLightningEvents = true)
{
var spriteState = LightningRandomizer();
_beam.TryCreateBeam(coordinates, target, lightningPrototype, spriteState);
if (triggerLightningEvents) // we don't want certain prototypes to trigger lightning level events
{
var ev = new HitByLightningEvent(null, target);
RaiseLocalEvent(target, ref ev);
}
}
/// <summary>
/// Fires lightning from coordinates to other coordinates
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
/// <param name="coordinates">Where the lightning fires from</param>
/// <param name="targetCoordinates">Where the lightning fires to</param>
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
/// <param name="triggerLightningEvents">if the lightnings being fired should trigger lightning events.</param>
public void ShootLightning(MapCoordinates coordinates, MapCoordinates targetCoordinates, string lightningPrototype = "Lightning", bool triggerLightningEvents = true)
{
var spriteState = LightningRandomizer();
_beam.TryCreateBeam(coordinates, targetCoordinates, lightningPrototype, spriteState);
}
/// <summary>
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
/// </summary>
/// <remarks>
/// Ported from imp.
/// </remarks>
/// <param name="coordinates">Where the lightning fires from</param>
/// <param name="range">Targets selection radius</param>
/// <param name="boltCount">Number of lightning bolts</param>
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
/// <param name="arcDepth">how many times to recursively fire lightning bolts from the target points of the first shot.</param>
/// <param name="triggerLightningEvents">if the lightnings being fired should trigger lightning events.</param>
/// <param name="hitCoordsChance">Chance for lightning to strike random coordinates instead of an entity.</param>
public void ShootRandomLightnings(MapCoordinates coordinates, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true, float hitCoordsChance = 0f, EntityUid? user = null)
{
//TODO: add support to different priority target tablem for different lightning types
//TODO: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent)
//TODO: This is still pretty bad for perf but better than before and at least it doesn't re-allocate
// several hashsets every time
var targets = _lookup.GetEntitiesInRange<LightningTargetComponent>(coordinates, range).ToList();
_random.Shuffle(targets);
targets.Sort((x, y) => y.Comp.Priority.CompareTo(x.Comp.Priority));
int shootedCount = 0;
int count = -1;
int mobLightningResistance = 2; // imp - If the target has a lightning resistance less than or equal to this, the lightning can hit random coordinates instead.
while (shootedCount < boltCount)
{
count++;
// imp - random coordinate lightning start
var outOfRange = count >= targets.Count;
var targetLightningResistance = outOfRange ? 0 : targets[count].Comp.LightningResistance;
if (hitCoordsChance > 0 && targetLightningResistance <= mobLightningResistance && _random.Prob(hitCoordsChance))
{
var targetCoordinate = coordinates.Offset(_random.NextVector2(range, range));
if (user != null)
ShootLightning(user.Value, targetCoordinate, lightningPrototype, triggerLightningEvents);
else
ShootLightning(coordinates, targetCoordinate, lightningPrototype, triggerLightningEvents);
if (arcDepth > 0)
{
ShootRandomLightnings(targetCoordinate, range, 1, lightningPrototype, arcDepth - 1, triggerLightningEvents, hitCoordsChance, user);
}
shootedCount++;
continue;
}
if (outOfRange) { break; }
// imp - random coordinate lightning end
var curTarget = targets[count];
if (!_random.Prob(curTarget.Comp.HitProbability)) //Chance to ignore target
continue;
// imp - use correct shoot lightning method based on whether this is coordinates-based or entity-based
if (user != null)
ShootLightning(user.Value, targets[count].Owner, lightningPrototype, triggerLightningEvents);
else
ShootLightning(coordinates, targets[count].Owner, lightningPrototype, triggerLightningEvents);
if (arcDepth - targetLightningResistance > 0)
{
ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targetLightningResistance, triggerLightningEvents, hitCoordsChance);
}
shootedCount++;
}
}
/// <summary>
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
@@ -69,45 +198,18 @@ public sealed class LightningSystem : SharedLightningSystem
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
/// <param name="arcDepth">how many times to recursively fire lightning bolts from the target points of the first shot.</param>
/// <param name="triggerLightningEvents">if the lightnings being fired should trigger lightning events.</param>
public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true)
/// <param name="hitCoordsChance">Chance for lightning to strike random coordinates instead of an entity.</param>
public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true, float hitCoordsChance = 0f)
{
//To Do: add support to different priority target tablem for different lightning types
//To Do: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent)
//To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate
// several hashsets every time
var mapCoords = _transform.GetMapCoordinates(user);
var targets = _lookup.GetEntitiesInRange<LightningTargetComponent>(mapCoords, range).ToList();
_random.Shuffle(targets);
targets.Sort((x, y) => y.Comp.Priority.CompareTo(x.Comp.Priority));
int shotCount = 0;
int count = -1;
while (shotCount < boltCount)
{
count++;
if (count >= targets.Count) { break; }
var curTarget = targets[count];
// Chance to ignore target
if (!_random.Prob(curTarget.Comp.HitProbability))
continue;
ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents);
if (arcDepth - targets[count].Comp.LightningResistance > 0)
ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].Comp.LightningResistance, triggerLightningEvents);
shotCount++;
}
// imp - Redirect to the other function for compatibility
ShootRandomLightnings(_transform.GetMapCoordinates(user), range, boltCount, lightningPrototype, arcDepth, triggerLightningEvents, hitCoordsChance, user);
}
}
/// <summary>
/// Raised directed on the target when an entity becomes the target of a lightning strike (not when touched)
/// </summary>
/// <param name="Source">The entity that created the lightning</param>
/// <param name="Source">The entity that created the lightning. May be null if the lightning came from coordinates rather than an entity.</param>
/// <param name="Target">The entity that was struck by lightning.</param>
[ByRefEvent]
public readonly record struct HitByLightningEvent(EntityUid Source, EntityUid Target);
public readonly record struct HitByLightningEvent(EntityUid? Source, EntityUid Target); // imp - Lightning might come from coordinates instead of an entity, so Source can be null

View File

@@ -0,0 +1,217 @@
using Content.Server.Pinpointer;
using Content.Shared._EE.Supermatter.Components;
using Content.Shared._EE.Supermatter.Consoles;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Content.Shared.Pinpointer;
using Content.Shared.Radiation.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server._EE.Supermatter.Console.Systems;
public sealed class SupermatterConsoleSystem : SharedSupermatterConsoleSystem
{
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly NavMapSystem _navMapSystem = default!;
private const float UpdateTime = 1.0f;
// Note: this data does not need to be saved
private float _updateTimer = 1.0f;
public override void Initialize()
{
base.Initialize();
// Console events
SubscribeLocalEvent<SupermatterConsoleComponent, ComponentInit>(OnConsoleInit);
SubscribeLocalEvent<SupermatterConsoleComponent, EntParentChangedMessage>(OnConsoleParentChanged);
SubscribeLocalEvent<SupermatterConsoleComponent, SupermatterConsoleFocusChangeMessage>(OnFocusChangedMessage);
// Grid events
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
}
#region Event handling
private void OnConsoleInit(EntityUid uid, SupermatterConsoleComponent component, ComponentInit args)
{
InitalizeConsole(uid, component);
}
private void OnConsoleParentChanged(EntityUid uid, SupermatterConsoleComponent component, EntParentChangedMessage args)
{
InitalizeConsole(uid, component);
}
private void OnFocusChangedMessage(EntityUid uid, SupermatterConsoleComponent component, SupermatterConsoleFocusChangeMessage args)
{
component.FocusSupermatter = args.FocusSupermatter;
}
private void OnGridSplit(ref GridSplitEvent args)
{
// Collect grids
var allGrids = args.NewGrids.ToList();
if (!allGrids.Contains(args.Grid))
allGrids.Add(args.Grid);
// Update supermatter monitoring consoles that stand upon an updated grid
var query = AllEntityQuery<SupermatterConsoleComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
{
if (entXform.GridUid == null)
continue;
if (!allGrids.Contains(entXform.GridUid.Value))
continue;
InitalizeConsole(ent, entConsole);
}
}
#endregion
public override void Update(float frameTime)
{
base.Update(frameTime);
// Keep a list of UI entries for each gridUid, in case multiple consoles stand on the same grid
var supermatterEntriesForEachGrid = new Dictionary<EntityUid, SupermatterConsoleEntry[]>();
var query = AllEntityQuery<SupermatterConsoleComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
{
if (entXform?.GridUid == null)
continue;
// Make a list of alarm state data for all the supermatters on the grid
if (!supermatterEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var supermatterEntries))
{
supermatterEntries = GetSupermatterStateData(entXform.GridUid.Value).ToArray();
supermatterEntriesForEachGrid[entXform.GridUid.Value] = supermatterEntries;
}
// Determine the highest level of status for the console
var highestStatus = SupermatterStatusType.Inactive;
foreach (var entry in supermatterEntries)
{
var status = entry.EntityStatus;
if (status > highestStatus)
highestStatus = status;
}
// Update the appearance of the console based on the highest recorded level of alert
if (TryComp<AppearanceComponent>(ent, out var entAppearance))
_appearance.SetData(ent, SupermatterConsoleVisuals.ComputerLayerScreen, (int)highestStatus, entAppearance);
// If the console UI is open, send UI data to each subscribed session
UpdateUIState(ent, supermatterEntries, entConsole, entXform);
}
}
public void UpdateUIState
(EntityUid uid,
SupermatterConsoleEntry[] supermatterStateData,
SupermatterConsoleComponent component,
TransformComponent xform)
{
if (!_userInterfaceSystem.IsUiOpen(uid, SupermatterConsoleUiKey.Key))
return;
var gridUid = xform.GridUid!.Value;
// Gathering remaining data to be send to the client
var focusSupermatterData = GetFocusSupermatterData(uid, GetEntity(component.FocusSupermatter), gridUid);
// Set the UI state
_userInterfaceSystem.SetUiState(uid, SupermatterConsoleUiKey.Key,
new SupermatterConsoleBoundInterfaceState(supermatterStateData, focusSupermatterData));
}
private List<SupermatterConsoleEntry> GetSupermatterStateData(EntityUid gridUid)
{
var supermatterStateData = new List<SupermatterConsoleEntry>();
var querySupermatters = AllEntityQuery<SupermatterComponent, TransformComponent>();
while (querySupermatters.MoveNext(out var ent, out var entSupermatter, out var entXform))
{
if (entXform.GridUid != gridUid)
continue;
if (!entXform.Anchored)
continue;
// Create entry
var netEnt = GetNetEntity(ent);
var entry = new SupermatterConsoleEntry
(netEnt,
MetaData(ent).EntityName,
entSupermatter.Status);
supermatterStateData.Add(entry);
}
return supermatterStateData;
}
private SupermatterFocusData? GetFocusSupermatterData(EntityUid uid, EntityUid? focusSupermatter, EntityUid gridUid)
{
if (focusSupermatter == null)
return null;
var focusSupermatterXform = Transform(focusSupermatter.Value);
if (!focusSupermatterXform.Anchored ||
focusSupermatterXform.GridUid != gridUid ||
!TryComp<SupermatterComponent>(focusSupermatter.Value, out var focusComp))
return null;
if (!TryComp<SupermatterComponent>(focusSupermatter.Value, out var sm))
return null;
if (!TryComp<RadiationSourceComponent>(focusSupermatter.Value, out var radiationComp))
return null;
var gases = sm.GasStorage;
var tempThreshold = Atmospherics.T0C + sm.HeatPenaltyThreshold;
return new SupermatterFocusData(
GetNetEntity(focusSupermatter.Value),
GetIntegrity(sm),
sm.Power,
radiationComp.Intensity,
gases.Sum(gas => gases[gas.Key]),
sm.Temperature,
tempThreshold * sm.DynamicHeatResistance,
sm.WasteMultiplier,
sm.GasEfficiency * 100,
sm.GasStorage);
}
public float GetIntegrity(SupermatterComponent sm)
{
var integrity = sm.Damage / sm.DamageDelaminationPoint;
integrity = (float)Math.Round(100 - integrity * 100, 2);
integrity = integrity < 0 ? 0 : integrity;
return integrity;
}
private void InitalizeConsole(EntityUid uid, SupermatterConsoleComponent component)
{
var xform = Transform(uid);
if (xform.GridUid == null)
return;
Dirty(uid, component);
}
}

View File

@@ -1,13 +1,25 @@
using Content.Shared.Atmos;
using Content.Shared.Radiation.Components;
using Content.Shared.Supermatter.Components;
using System.Text;
using Content.Shared.Chat;
using System.Linq;
using System.Text;
using Content.Server.Chat.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Sound.Components;
using Content.Shared._EE.CCVars;
using Content.Shared._EE.Supermatter.Components;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Popups;
using Content.Shared.Radiation.Components;
using Content.Shared.Speech;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Supermatter.Systems;
namespace Content.Server._EE.Supermatter.Systems;
public sealed partial class SupermatterSystem
{
@@ -97,11 +109,16 @@ public sealed partial class SupermatterSystem
// Irradiate stuff
if (TryComp<RadiationSourceComponent>(uid, out var rad))
{
rad.Intensity =
sm.Power
_config.GetCVar(ECCVars.SupermatterRadsBase) +
(sm.Power
* Math.Max(0, 1f + transmissionBonus / 10f)
* 0.003f
* _config.GetCVar(CCVars.SupermatterRadsModifier);
* _config.GetCVar(ECCVars.SupermatterRadsModifier));
rad.Slope = Math.Clamp(rad.Intensity / 15, 0.2f, 1f);
}
// Power * 0.55 * 0.8~1
// This has to be differentiated with respect to time, since its going to be interacting with systems
@@ -126,6 +143,14 @@ public sealed partial class SupermatterSystem
// After this point power is lowered
// This wraps around to the begining of the function
sm.Power = Math.Max(sm.Power - Math.Min(powerReduction * powerlossInhibitor, sm.Power * 0.83f * powerlossInhibitor), 0f);
// Save values to the supermatter
sm.GasStorage = sm.GasStorage.ToDictionary(
gas => gas.Key,
gas => absorbedGas.GetMoles(gas.Key)
);
sm.Temperature = absorbedGas.Temperature;
sm.WasteMultiplier = heatModifier;
}
/// <summary>
@@ -133,10 +158,35 @@ public sealed partial class SupermatterSystem
/// </summary>
private void SupermatterZap(EntityUid uid, SupermatterComponent sm)
{
// Divide power by its' threshold to get a value from 0-1, then multiply by the amount of possible lightnings
var zapPower = sm.Power / sm.PowerPenaltyThreshold * sm.LightningPrototypes.Length;
var zapPowerNorm = (int) Math.Clamp(zapPower, 0, sm.LightningPrototypes.Length - 1);
_lightning.ShootRandomLightnings(uid, 3.5f, sm.Power > sm.PowerPenaltyThreshold ? 3 : 1, sm.LightningPrototypes[zapPowerNorm]);
var zapPower = 0;
var zapCount = 0;
var zapRange = Math.Clamp(sm.Power / 1000, 2, 7);
// fuck this
if (_random.Prob(0.05f))
{
zapCount += 1;
}
if (sm.Power >= sm.PowerPenaltyThreshold)
{
zapCount += 2;
}
if (sm.Power >= sm.SeverePowerPenaltyThreshold)
{
zapPower = 1;
zapCount++;
}
if (sm.Power >= sm.CriticalPowerPenaltyThreshold)
{
zapPower = 2;
zapCount++;
}
if (zapCount >= 1)
_lightning.ShootRandomLightnings(uid, zapRange, zapCount, sm.LightningPrototypes[zapPower], hitCoordsChance: sm.ZapHitCoordinatesChance);
}
/// <summary>
@@ -184,20 +234,16 @@ public sealed partial class SupermatterSystem
totalDamage += powerDamage;
// Mol count only starts affecting damage when it is above 1800
var moleDamage = Math.Max(moles - sm.MolePenaltyThreshold, 0) / 80 * sm.DamageIncreaseMultiplier;
var moleDamage = Math.Max(moles - sm.MolePenaltyThreshold, 0f) / 80 * sm.DamageIncreaseMultiplier;
totalDamage += moleDamage;
// Healing damage
if (moles < sm.MolePenaltyThreshold)
{
// There's a very small float so that it doesn't divide by 0
var healHeatDamage = Math.Min(absorbedGas.Temperature - tempThreshold, 0.001f) / 150;
var healHeatDamage = Math.Min(absorbedGas.Temperature - tempThreshold, 0f) / 150;
totalDamage += healHeatDamage;
}
// Return the manipulated gas back to the mix
_atmosphere.Merge(mix, absorbedGas);
// Check for space tiles next to SM
//TODO: Change moles out for checking if adjacent tiles exist
var enumerator = _atmosphere.GetAdjacentTileMixtures(xform.GridUid.Value, indices, false, false);
@@ -217,12 +263,12 @@ public sealed partial class SupermatterSystem
_ => 0f
};
totalDamage += Math.Clamp(sm.Power * factor * sm.DamageIncreaseMultiplier, 0, sm.MaxSpaceExposureDamage);
totalDamage += Math.Clamp(sm.Power * factor * sm.DamageIncreaseMultiplier, 0f, sm.MaxSpaceExposureDamage);
break;
}
var damage = Math.Min(sm.DamageArchived + sm.DamageHardcap * sm.DamageDelaminationPoint, totalDamage);
var damage = Math.Min(sm.DamageArchived + sm.DamageHardcap * sm.DamageDelaminationPoint, sm.Damage + totalDamage);
// Prevent it from going negative
sm.Damage = Math.Clamp(damage, 0, float.PositiveInfinity);
@@ -233,19 +279,16 @@ public sealed partial class SupermatterSystem
/// </summary>
private void AnnounceCoreDamage(EntityUid uid, SupermatterComponent sm)
{
// If undamaged, no need to announce anything
if (sm.Damage == 0)
return;
var message = string.Empty;
var global = false;
var integrity = GetIntegrity(sm).ToString("0.00");
// Special cases
if (sm.Damage < sm.DamageDelaminationPoint && sm.Delamming)
{
message = Loc.GetString("supermatter-delam-cancel", ("integrity", integrity));
sm.DelamAnnounced = false;
global = true;
}
// Instantly announce delamination
if (sm.Delamming && !sm.DelamAnnounced)
{
var sb = new StringBuilder();
@@ -259,18 +302,73 @@ public sealed partial class SupermatterSystem
default: loc = "supermatter-delam-explosion"; break;
}
var station = _station.GetOwningStation(uid);
if (station != null)
_alert.SetLevel((EntityUid) station, sm.AlertCodeDeltaId, true, true, true, false);
sb.AppendLine(Loc.GetString(loc));
sb.AppendLine(Loc.GetString("supermatter-seconds-before-delam", ("seconds", sm.DelamTimer)));
sb.Append(Loc.GetString("supermatter-seconds-before-delam", ("seconds", sm.DelamTimer)));
message = sb.ToString();
global = true;
sm.DelamAnnounced = true;
sm.YellTimer = TimeSpan.FromSeconds(sm.DelamTimer / 2);
SendSupermatterAnnouncement(uid, message, global);
SendSupermatterAnnouncement(uid, sm, message, global);
return;
}
// Only announce every YellTimer seconds
if (_timing.CurTime < sm.YellLast + sm.YellTimer)
return;
// Recovered after the delamination point
if (sm.Damage < sm.DamageDelaminationPoint && sm.DelamAnnounced)
{
message = Loc.GetString("supermatter-delam-cancel", ("integrity", integrity));
sm.DelamAnnounced = false;
sm.YellTimer = TimeSpan.FromSeconds(_config.GetCVar(ECCVars.SupermatterYellTimer));
global = true;
SendSupermatterAnnouncement(uid, sm, message, global);
return;
}
// Oh god oh fuck
if (sm.Delamming && sm.DelamAnnounced)
{
var seconds = Math.Ceiling(sm.DelamEndTime.TotalSeconds - _timing.CurTime.TotalSeconds);
if (seconds <= 0)
return;
var loc = seconds switch
{
> 5 => "supermatter-seconds-before-delam-countdown",
<= 5 => "supermatter-seconds-before-delam-imminent",
_ => String.Empty
};
sm.YellTimer = seconds switch
{
> 30 => TimeSpan.FromSeconds(10),
> 5 => TimeSpan.FromSeconds(5),
<= 5 => TimeSpan.FromSeconds(1),
_ => TimeSpan.FromSeconds(_config.GetCVar(ECCVars.SupermatterYellTimer))
};
message = Loc.GetString(loc, ("seconds", seconds));
global = true;
SendSupermatterAnnouncement(uid, sm, message, global);
return;
}
// We're safe
if (sm.Damage < sm.DamageArchived && sm.Status >= SupermatterStatusType.Warning)
{
message = Loc.GetString("supermatter-healing", ("integrity", integrity));
if (sm.Status >= SupermatterStatusType.Emergency)
global = true;
SendSupermatterAnnouncement(uid, sm, message, global);
return;
}
@@ -292,21 +390,27 @@ public sealed partial class SupermatterSystem
}
}
SendSupermatterAnnouncement(uid, message, global);
SendSupermatterAnnouncement(uid, sm, message, global);
}
/// <param name="global">If true, sends a station announcement</param>
/// <param name="global">If true, sends the message to the common radio</param>
/// <param name="customSender">Localisation string for a custom announcer name</param>
public void SendSupermatterAnnouncement(EntityUid uid, string message, bool global = false, string? customSender = null)
public void SendSupermatterAnnouncement(EntityUid uid, SupermatterComponent sm, string message, bool global = false)
{
if (global)
{
var sender = Loc.GetString(customSender != null ? customSender : "supermatter-announcer");
_chat.DispatchStationAnnouncement(uid, message, sender, colorOverride: Color.Yellow);
if (message == String.Empty)
return;
}
var channel = sm.Channel;
if (global)
channel = sm.ChannelGlobal;
// Ensure status, otherwise the wrong speech sound may be used
HandleStatus(uid, sm);
sm.YellLast = _timing.CurTime;
_chat.TrySendInGameICMessage(uid, message, InGameICChatType.Speak, hideChat: false, checkRadioPrefix: true);
_radio.SendRadioMessage(uid, message, channel, uid);
}
/// <summary>
@@ -325,8 +429,8 @@ public sealed partial class SupermatterSystem
/// </summary>
public DelamType ChooseDelamType(EntityUid uid, SupermatterComponent sm)
{
if (_config.GetCVar(CCVars.SupermatterDoForceDelam))
return _config.GetCVar(CCVars.SupermatterForcedDelamType);
if (_config.GetCVar(ECCVars.SupermatterDoForceDelam))
return _config.GetCVar(ECCVars.SupermatterForcedDelamType);
var mix = _atmosphere.GetContainingMixture(uid, true, true);
@@ -335,13 +439,13 @@ public sealed partial class SupermatterSystem
var absorbedGas = mix.Remove(sm.GasEfficiency * mix.TotalMoles);
var moles = absorbedGas.TotalMoles;
if (_config.GetCVar(CCVars.SupermatterDoSingulooseDelam)
&& moles >= sm.MolePenaltyThreshold * _config.GetCVar(CCVars.SupermatterSingulooseMolesModifier))
if (_config.GetCVar(ECCVars.SupermatterDoSingulooseDelam)
&& moles >= sm.MolePenaltyThreshold * _config.GetCVar(ECCVars.SupermatterSingulooseMolesModifier))
return DelamType.Singulo;
}
if (_config.GetCVar(CCVars.SupermatterDoTeslooseDelam)
&& sm.Power >= sm.PowerPenaltyThreshold * _config.GetCVar(CCVars.SupermatterTesloosePowerModifier))
if (_config.GetCVar(ECCVars.SupermatterDoTeslooseDelam)
&& sm.Power >= sm.PowerPenaltyThreshold * _config.GetCVar(ECCVars.SupermatterTesloosePowerModifier))
return DelamType.Tesla;
//TODO: Add resonance cascade when there's crazy conditions or a destabilizing crystal
@@ -361,6 +465,7 @@ public sealed partial class SupermatterSystem
if (!sm.Delamming)
{
sm.Delamming = true;
sm.DelamEndTime = _timing.CurTime + TimeSpan.FromSeconds(sm.DelamTimer);
AnnounceCoreDamage(uid, sm);
}
@@ -370,11 +475,23 @@ public sealed partial class SupermatterSystem
AnnounceCoreDamage(uid, sm);
}
sm.DelamTimerAccumulator++;
if (sm.DelamTimerAccumulator < sm.DelamTimer)
if (_timing.CurTime < sm.DelamEndTime)
return;
var smTransform = Transform(uid);
foreach (var pSession in Filter.GetAllPlayers())
{
var pEntity = pSession.AttachedEntity;
if (pEntity != null
&& TryComp<TransformComponent>(pEntity, out var pTransform)
&& pTransform.MapID == smTransform.MapID)
_popup.PopupEntity(Loc.GetString("supermatter-delam-player"), pEntity.Value, pEntity.Value, PopupType.MediumCaution);
}
_audio.PlayGlobal(sm.DistortSound, Filter.BroadcastMap(Transform(uid).MapID), true);
switch (sm.PreferredDelamType)
{
case DelamType.Cascade:
@@ -395,6 +512,55 @@ public sealed partial class SupermatterSystem
}
}
/// <summary>
/// Sets the supermatter's status and speech sound based on thresholds
/// </summary>
private void HandleStatus(EntityUid uid, SupermatterComponent sm)
{
var currentStatus = GetStatus(uid, sm);
if (sm.Status != currentStatus)
{
sm.Status = currentStatus;
if (!TryComp<SpeechComponent>(uid, out var speech))
return;
sm.StatusCurrentSound = currentStatus switch
{
SupermatterStatusType.Warning => sm.StatusWarningSound,
SupermatterStatusType.Danger => sm.StatusDangerSound,
SupermatterStatusType.Emergency => sm.StatusEmergencySound,
SupermatterStatusType.Delaminating => sm.StatusDelamSound,
_ => null
};
ProtoId<SpeechSoundsPrototype>? speechSound = sm.StatusCurrentSound;
if (currentStatus == SupermatterStatusType.Warning)
speech.AudioParams = AudioParams.Default.AddVolume(7.5f);
else
speech.AudioParams = AudioParams.Default.AddVolume(10f);
if (currentStatus == SupermatterStatusType.Delaminating)
speech.SoundCooldownTime = 6.8f; // approximate length of bloblarm.ogg
else
speech.SoundCooldownTime = 0.0f;
speech.SpeechSounds = speechSound;
}
// Supermatter is healing, don't play any speech sounds
if (sm.Damage < sm.DamageArchived)
{
if (!TryComp<SpeechComponent>(uid, out var speech))
return;
sm.StatusCurrentSound = null;
speech.SpeechSounds = null;
}
}
/// <summary>
/// Swaps out ambience sounds when the SM is delamming or not.
/// </summary>
@@ -405,13 +571,48 @@ public sealed partial class SupermatterSystem
if (ambient == null)
return;
if (sm.Delamming && sm.CurrentSoundLoop != sm.DelamSound)
sm.CurrentSoundLoop = sm.DelamSound;
var volume = (float) Math.Round(Math.Clamp((sm.Power / 50) - 5, -5, 5));
else if (!sm.Delamming && sm.CurrentSoundLoop != sm.CalmSound)
sm.CurrentSoundLoop = sm.CalmSound;
_ambient.SetVolume(uid, volume);
if (sm.Status >= SupermatterStatusType.Danger && sm.CurrentSoundLoop != sm.DelamLoopSound)
sm.CurrentSoundLoop = sm.DelamLoopSound;
else if (sm.Status < SupermatterStatusType.Danger && sm.CurrentSoundLoop != sm.CalmLoopSound)
sm.CurrentSoundLoop = sm.CalmLoopSound;
if (ambient.Sound != sm.CurrentSoundLoop)
_ambient.SetSound(uid, sm.CurrentSoundLoop, ambient);
}
/// <summary>
/// Plays normal/delam sounds at a rate determined by power and damage
/// </summary>
private void HandleAccent(EntityUid uid, SupermatterComponent sm)
{
var emit = Comp<EmitSoundOnTriggerComponent>(uid);
if (emit == null)
return;
if (sm.AccentLastTime >= _timing.CurTime || !_random.Prob(0.05f))
return;
var aggression = Math.Min((sm.Damage / 800) * (sm.Power / 2500), 1) * 100;
var nextSound = Math.Max(Math.Round((100 - aggression) * 5), sm.AccentMinCooldown);
if (sm.AccentLastTime + TimeSpan.FromSeconds(nextSound) > _timing.CurTime)
return;
if (sm.Status >= SupermatterStatusType.Danger && emit.Sound != sm.DelamAccent)
emit.Sound = sm.DelamAccent;
else if (sm.Status < SupermatterStatusType.Danger && emit.Sound != sm.CalmAccent)
emit.Sound = sm.CalmAccent;
sm.AccentLastTime = _timing.CurTime;
var ev = new TriggerEvent(uid);
RaiseLocalEvent(uid, ev);
}
}

View File

@@ -1,33 +1,46 @@
using Content.Server.AlertLevel;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Chat.Systems;
using Content.Server.Decals;
using Content.Server.DoAfter;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Kitchen.Components;
using Content.Server.Lightning;
using Content.Server.Lightning.Components;
using Content.Server.Popups;
using Content.Server.Radio.EntitySystems;
using Content.Server.Speech;
using Content.Server.Station.Systems;
using Content.Shared._EE.CCVars;
using Content.Shared._EE.Supermatter.Components;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Content.Shared.Audio;
using Content.Shared.Body.Components;
using Content.Shared.CCVar;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
using Content.Shared.Projectiles;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Containers;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Events;
using Robust.Server.GameObjects;
using Content.Shared.Atmos;
using Content.Shared.Interaction;
using Content.Shared.Projectiles;
using Content.Shared.Mobs.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Chat.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Supermatter.Components;
using Content.Server.Lightning;
using Content.Server.AlertLevel;
using Content.Server.Station.Systems;
using Content.Server.Kitchen.Components;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Server.DoAfter;
using Content.Server.Popups;
using Content.Shared.Audio;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Supermatter.Systems;
namespace Content.Server._EE.Supermatter.Systems;
public sealed partial class SupermatterSystem : EntitySystem
{
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ExplosionSystem _explosion = default!;
[Dependency] private readonly TransformSystem _xform = default!;
@@ -36,10 +49,13 @@ public sealed partial class SupermatterSystem : EntitySystem
[Dependency] private readonly LightningSystem _lightning = default!;
[Dependency] private readonly AlertLevelSystem _alert = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
@@ -47,6 +63,7 @@ public sealed partial class SupermatterSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<SupermatterComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SupermatterComponent, AtmosDeviceUpdateEvent>(OnSupermatterUpdated);
SubscribeLocalEvent<SupermatterComponent, StartCollideEvent>(OnCollideEvent);
SubscribeLocalEvent<SupermatterComponent, InteractHandEvent>(OnHandInteract);
@@ -55,56 +72,22 @@ public sealed partial class SupermatterSystem : EntitySystem
SubscribeLocalEvent<SupermatterComponent, SupermatterDoAfterEvent>(OnGetSliver);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var sm in EntityManager.EntityQuery<SupermatterComponent>())
{
if (!sm.Activated)
return;
var uid = sm.Owner;
sm.UpdateAccumulator += frameTime;
if (sm.UpdateAccumulator >= sm.UpdateTimer)
{
sm.UpdateAccumulator -= sm.UpdateTimer;
Cycle(uid, sm, frameTime);
}
}
}
public void Cycle(EntityUid uid, SupermatterComponent sm, float frameTime)
{
sm.ZapAccumulator++;
sm.YellAccumulator++;
ProcessAtmos(uid, sm, frameTime);
HandleDamage(uid, sm);
if (sm.Damage >= sm.DamageDelaminationPoint || sm.Delamming)
HandleDelamination(uid, sm);
HandleSoundLoop(uid, sm);
if (sm.ZapAccumulator >= sm.ZapTimer)
{
sm.ZapAccumulator -= sm.ZapTimer;
SupermatterZap(uid, sm);
}
if (sm.YellAccumulator >= sm.YellTimer)
{
sm.YellAccumulator -= sm.YellTimer;
AnnounceCoreDamage(uid, sm);
}
}
private void OnMapInit(EntityUid uid, SupermatterComponent sm, MapInitEvent args)
{
// Set the yell timer
sm.YellTimer = TimeSpan.FromSeconds(_config.GetCVar(ECCVars.SupermatterYellTimer));
// Set the Sound
_ambient.SetAmbience(uid, true);
@@ -113,6 +96,21 @@ public sealed partial class SupermatterSystem : EntitySystem
mix?.AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard);
mix?.AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard);
}
public void OnSupermatterUpdated(EntityUid uid, SupermatterComponent sm, AtmosDeviceUpdateEvent args)
{
ProcessAtmos(uid, sm, args.dt);
HandleDamage(uid, sm);
if (sm.Damage >= sm.DamageDelaminationPoint || sm.Delamming)
HandleDelamination(uid, sm);
HandleStatus(uid, sm);
HandleSoundLoop(uid, sm);
HandleAccent(uid, sm);
if (sm.Damage >= sm.DamagePenaltyPoint)
SupermatterZap(uid, sm);
}
private void OnCollideEvent(EntityUid uid, SupermatterComponent sm, ref StartCollideEvent args)
{
@@ -127,12 +125,26 @@ public sealed partial class SupermatterSystem : EntitySystem
if (!HasComp<ProjectileComponent>(target))
{
EntityManager.SpawnEntity(sm.CollisionResultPrototype, Transform(target).Coordinates);
_audio.PlayPvs(sm.DustSound, uid);
var popup = "supermatter-collide";
if (HasComp<MobStateComponent>(target))
{
popup = "supermatter-collide-mob";
EntityManager.SpawnEntity(sm.CollisionResultPrototype, Transform(target).Coordinates);
}
var targetProto = MetaData(target).EntityPrototype;
if (targetProto != null && targetProto.ID != sm.CollisionResultPrototype)
{
_popup.PopupEntity(Loc.GetString(popup, ("sm", uid), ("target", target)), uid, PopupType.LargeCaution);
_audio.PlayPvs(sm.DustSound, uid);
}
sm.Power += args.OtherBody.Mass;
}
EntityManager.QueueDeleteEntity(target);
AddComp<SupermatterImmuneComponent>(target); // prevent spam or excess power production
if (TryComp<SupermatterFoodComponent>(target, out var food))
sm.Power += food.Energy;
@@ -157,6 +169,7 @@ public sealed partial class SupermatterSystem : EntitySystem
sm.MatterPower += 200;
EntityManager.SpawnEntity(sm.CollisionResultPrototype, Transform(target).Coordinates);
_popup.PopupEntity(Loc.GetString("supermatter-collide-mob", ("sm", uid), ("target", target)), uid, PopupType.LargeCaution);
_audio.PlayPvs(sm.DustSound, uid);
EntityManager.QueueDeleteEntity(target);
}
@@ -176,7 +189,6 @@ public sealed partial class SupermatterSystem : EntitySystem
{
BreakOnDamage = true,
BreakOnHandChange = false,
BreakOnMove = true,
BreakOnWeightlessMove = false,
NeedHand = true,
RequireCanInteract = true,
@@ -195,7 +207,7 @@ public sealed partial class SupermatterSystem : EntitySystem
sm.Damage += sm.DamageDelaminationPoint / 10;
var integrity = GetIntegrity(sm).ToString("0.00");
SendSupermatterAnnouncement(uid, Loc.GetString("supermatter-announcement-cc-tamper", ("integrity", integrity)), true, "Central Command");
SendSupermatterAnnouncement(uid, sm, Loc.GetString("supermatter-announcement-cc-tamper", ("integrity", integrity)));
Spawn(sm.SliverPrototype, _transform.GetMapCoordinates(args.User));
_popup.PopupClient(Loc.GetString("supermatter-tamper-end"), uid, args.User);
@@ -208,4 +220,32 @@ public sealed partial class SupermatterSystem : EntitySystem
if (args.IsInDetailsRange)
args.PushMarkup(Loc.GetString("supermatter-examine-integrity", ("integrity", GetIntegrity(sm).ToString("0.00"))));
}
private SupermatterStatusType GetStatus(EntityUid uid, SupermatterComponent sm)
{
var mix = _atmosphere.GetContainingMixture(uid, true, true);
if (mix is not { })
return SupermatterStatusType.Error;
if (sm.Delamming || sm.Damage >= sm.DamageDelaminationPoint)
return SupermatterStatusType.Delaminating;
if (sm.Damage >= sm.DamagePenaltyPoint)
return SupermatterStatusType.Emergency;
if (sm.Damage >= sm.DamageDelamAlertPoint)
return SupermatterStatusType.Danger;
if (sm.Damage >= sm.DamageWarningThreshold)
return SupermatterStatusType.Warning;
if (mix.Temperature > Atmospherics.T0C + (sm.HeatPenaltyThreshold * 0.8))
return SupermatterStatusType.Caution;
if (sm.Power > 5)
return SupermatterStatusType.Normal;
return SupermatterStatusType.Inactive;
}
}

View File

@@ -1,4 +1,5 @@
using Robust.Shared.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
namespace Content.Shared.Beam.Components;
@@ -44,6 +45,16 @@ public abstract partial class SharedBeamComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
[DataField("sound")]
public SoundSpecifier? Sound;
/// <summary>
/// Allow the sprite to be randomized.
/// </summary>
/// <remarks>
/// Ported from imp
/// </remarks>
[ViewVariables]
[DataField("allowSpriteOverwrite")]
public bool AllowSpriteOverwrite = true;
}
/// <summary>
@@ -68,13 +79,29 @@ public sealed class BeamControllerCreatedEvent : EntityEventArgs
public sealed class CreateBeamSuccessEvent : EntityEventArgs
{
public readonly EntityUid User;
public readonly EntityUid Target;
/// <summary>
/// The entity the beam targeted.
/// Imp - This may be null if the beam targeted a map coordinate.
/// </summary>
public readonly EntityUid? Target;
/// <summary>
/// The coordinates the beam targeted. This may be null if the beam targeted an entity.
/// </summary>
public readonly MapCoordinates? Coordinates;
public CreateBeamSuccessEvent(EntityUid user, EntityUid target)
{
User = user;
Target = target;
}
public CreateBeamSuccessEvent(EntityUid user, MapCoordinates coordinates)
{
User = user;
Coordinates = coordinates;
}
}
/// <summary>

View File

@@ -1,9 +1,11 @@
using Content.Shared.Supermatter.Components;
using Content.Shared._EE.Supermatter.Components;
using Robust.Shared.Configuration;
namespace Content.Shared.CCVar;
namespace Content.Shared._EE.CCVars;
public sealed partial class CCVars
[CVarDefs]
// ReSharper disable once InconsistentNaming
public sealed partial class ECCVars
{
/// <summary>
/// With completely default supermatter values, Singuloose delamination will occur if engineers inject at least 900 moles of coolant per tile
@@ -44,9 +46,21 @@ public sealed partial class CCVars
public static readonly CVarDef<DelamType> SupermatterForcedDelamType =
CVarDef.Create("supermatter.forced_delam_type", DelamType.Singulo, CVar.SERVER);
/// <summary>
/// Base amount of radiation that the supermatter emits.
/// </summary>
public static readonly CVarDef<float> SupermatterRadsBase =
CVarDef.Create("supermatter.rads_base", 3f, CVar.SERVER);
/// <summary>
/// Directly multiplies the amount of rads put out by the supermatter. Be VERY conservative with this.
/// </summary>
public static readonly CVarDef<float> SupermatterRadsModifier =
CVarDef.Create("supermatter.rads_modifier", 1f, CVar.SERVER);
/// <summary>
/// How often the supermatter should announce its status.
/// </summary>
public static readonly CVarDef<float> SupermatterYellTimer =
CVarDef.Create("supermatter.yell_timer", 60f, CVar.SERVER);
}

View File

@@ -1,11 +1,14 @@
using Robust.Shared.GameStates;
using Robust.Shared.Audio;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Content.Shared.Whitelist;
using Content.Shared.DoAfter;
using Content.Shared.Radio;
using Content.Shared.Speech;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Supermatter.Components;
namespace Content.Shared._EE.Supermatter.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class SupermatterComponent : Component
@@ -16,7 +19,13 @@ public sealed partial class SupermatterComponent : Component
/// The SM will only cycle if activated.
/// </summary>
[DataField]
public bool Activated = false;
public bool Activated = true;
/// <summary>
/// The current status of the singularity, used for alert sounds and the monitoring console
/// </summary>
[DataField]
public SupermatterStatusType Status = SupermatterStatusType.Inactive;
[DataField]
public string SliverPrototype = "SupermatterSliver";
@@ -26,14 +35,13 @@ public sealed partial class SupermatterComponent : Component
/// If removed - delamination timer is divided by 2.
/// </summary>
[DataField]
public bool SliverRemoved = false;
public bool SliverRemoved;
public string[] LightningPrototypes =
{
"Lightning",
"ChargedLightning",
"SuperchargedLightning",
"HyperchargedLightning"
"SupermatterLightning",
"SupermatterLightningCharged",
"SupermatterLightningSupercharged"
};
[DataField]
@@ -51,17 +59,45 @@ public sealed partial class SupermatterComponent : Component
[DataField]
public string CollisionResultPrototype = "Ash";
[DataField]
public SoundSpecifier DustSound = new SoundPathSpecifier("/Audio/Effects/Grenades/Supermatter/supermatter_start.ogg");
#endregion
#region Sounds
[DataField]
public SoundSpecifier CalmSound = new SoundPathSpecifier("/Audio/Supermatter/calm.ogg");
public SoundSpecifier DustSound = new SoundPathSpecifier("/Audio/_EE/Supermatter/supermatter.ogg");
[DataField]
public SoundSpecifier DelamSound = new SoundPathSpecifier("/Audio/Supermatter/delamming.ogg");
public SoundSpecifier DistortSound = new SoundPathSpecifier("/Audio/_EE/Supermatter/distort.ogg");
[DataField]
public SoundSpecifier CurrentSoundLoop = new SoundPathSpecifier("/Audio/Supermatter/calm.ogg");
public SoundSpecifier CalmLoopSound = new SoundPathSpecifier("/Audio/_EE/Supermatter/calm.ogg");
[DataField]
public SoundSpecifier DelamLoopSound = new SoundPathSpecifier("/Audio/_EE/Supermatter/delamming.ogg");
[DataField]
public SoundSpecifier CurrentSoundLoop = new SoundPathSpecifier("/Audio/_EE/Supermatter/calm.ogg");
[DataField]
public SoundSpecifier CalmAccent = new SoundCollectionSpecifier("SupermatterAccentNormal");
[DataField]
public SoundSpecifier DelamAccent = new SoundCollectionSpecifier("SupermatterAccentDelam");
[DataField]
public string StatusWarningSound = "SupermatterWarning";
[DataField]
public string StatusDangerSound = "SupermatterDanger";
[DataField]
public string StatusEmergencySound = "SupermatterEmergency";
[DataField]
public string StatusDelamSound = "SupermatterDelaminating";
[DataField]
public string? StatusCurrentSound;
#endregion
@@ -70,6 +106,12 @@ public sealed partial class SupermatterComponent : Component
[DataField]
public float Power;
[DataField]
public float Temperature;
[DataField]
public float WasteMultiplier;
[DataField]
public float MatterPower;
@@ -134,6 +176,12 @@ public sealed partial class SupermatterComponent : Component
[DataField]
public float OxygenReleaseEfficiencyModifier = 0.0031f;
/// <summary>
/// The chance for supermatter lightning to strike random coordinates instead of an entity
/// </summary>
[DataField]
public float ZapHitCoordinatesChance = 0.75f;
#endregion
#region Timing
@@ -142,43 +190,40 @@ public sealed partial class SupermatterComponent : Component
/// We yell if over 50 damage every YellTimer Seconds
/// </summary>
[DataField]
public float YellTimer = 60f;
public TimeSpan YellTimer = TimeSpan.Zero;
/// <summary>
/// Set to YellTimer at first so it doesnt yell a minute after being hit
/// Last time the supermatter's damage was announced
/// </summary>
[DataField]
public float YellAccumulator = 60f;
public TimeSpan YellLast = TimeSpan.Zero;
/// <summary>
/// Timer for delam
/// Time when the delamination will occuer
/// </summary>
[DataField]
public float DelamTimerAccumulator;
public TimeSpan DelamEndTime;
/// <summary>
/// Time until delam
/// How long it takes in seconds for the supermatter to delaminate after reaching zero integrity
/// </summary>
[DataField]
public float DelamTimer = 120f;
public float DelamTimer = 30f;
/// <summary>
/// The message timer
/// Last time a supermatter accent sound was triggered
/// </summary>
[DataField]
public float SpeakAccumulator = 60f;
public TimeSpan AccentLastTime = TimeSpan.Zero;
/// <summary>
/// Minimum time in seconds between supermatter accent sounds
/// </summary>
[DataField]
public float AccentMinCooldown = 2f;
[DataField]
public float UpdateAccumulator = 0f;
[DataField]
public float UpdateTimer = 1f;
[DataField]
public float ZapAccumulator = 0f;
[DataField]
public float ZapTimer = 10f;
public TimeSpan ZapLast = TimeSpan.Zero;
#endregion
@@ -213,7 +258,7 @@ public sealed partial class SupermatterComponent : Component
/// Above this value we can get lord singulo and independent mol damage, below it we can heal damage
/// </summary>
[DataField]
public float MolePenaltyThreshold = 900f;
public float MolePenaltyThreshold = 1800f;
/// <summary>
/// More moles of gases are harder to heat than fewer, so let's scale heat damage around them
@@ -226,7 +271,19 @@ public sealed partial class SupermatterComponent : Component
/// and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity
/// </summary>
[DataField]
public float PowerPenaltyThreshold = 4000f;
public float PowerPenaltyThreshold = 5000f;
/// <summary>
/// +1 bolt of electricity, TODO: anomaly spawning
/// </summary>
[DataField]
public float SeverePowerPenaltyThreshold = 7000f;
/// <summary>
/// +1 bolt of electricity
/// </summary>
[DataField]
public float CriticalPowerPenaltyThreshold = 9000f;
/// <summary>
/// Maximum safe operational temperature in degrees Celsius.
@@ -243,14 +300,14 @@ public sealed partial class SupermatterComponent : Component
/// The amount of damage taken
/// </summary>
[DataField]
public float Damage = 0f;
public float Damage;
/// <summary>
/// The damage from before this cycle.
/// Used to limit the damage we can take each cycle, and for safe alert.
/// </summary>
[DataField]
public float DamageArchived = 0f;
public float DamageArchived;
/// <summary>
/// Is multiplied by ExplosionPoint to cap evironmental damage per cycle
@@ -282,14 +339,26 @@ public sealed partial class SupermatterComponent : Component
[DataField]
public float DamageEmergencyThreshold = 500;
/// <summary>
/// The point at which the SM begins shooting lightning.
/// </summary>
[DataField]
public int DamagePenaltyPoint = 550;
/// <summary>
/// The point at which the SM begins delaminating.
/// </summary>
[DataField]
public int DamageDelaminationPoint = 900;
/// <summary>
/// The point at which the SM begins showing warning signs.
/// </summary>
[DataField]
public bool Delamming = false;
public int DamageDelamAlertPoint = 300;
[DataField]
public bool Delamming;
[DataField]
public DelamType PreferredDelamType = DelamType.Explosion;
@@ -299,13 +368,13 @@ public sealed partial class SupermatterComponent : Component
#region Announcements
[DataField]
public string AlertCodeYellowId = "yellow";
public bool DelamAnnounced;
[DataField]
public string AlertCodeDeltaId = "delta";
public ProtoId<RadioChannelPrototype> Channel = "Engineering";
[DataField]
public bool DelamAnnounced = false;
public ProtoId<RadioChannelPrototype> ChannelGlobal = "Common";
#endregion

View File

@@ -1,4 +1,4 @@
namespace Content.Shared.Supermatter.Components;
namespace Content.Shared._EE.Supermatter.Components;
[RegisterComponent]
public sealed partial class SupermatterFoodComponent : Component

View File

@@ -1,6 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Supermatter.Components;
namespace Content.Shared._EE.Supermatter.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class SupermatterImmuneComponent : Component

View File

@@ -0,0 +1,3 @@
namespace Content.Shared._EE.Supermatter.Consoles;
public abstract partial class SharedSupermatterConsoleSystem : EntitySystem
{ }

View File

@@ -0,0 +1,208 @@
using Content.Shared._EE.Supermatter.Consoles;
using Content.Shared._EE.Supermatter.Monitor;
using Content.Shared.Atmos;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
namespace Content.Shared._EE.Supermatter.Components;
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedSupermatterConsoleSystem))]
public sealed partial class SupermatterConsoleComponent : Component
{
/// <summary>
/// The current entity of interest (selected via the console UI)
/// </summary>
[ViewVariables]
public NetEntity? FocusSupermatter;
}
[Serializable, NetSerializable]
public struct SupermatterNavMapData
{
/// <summary>
/// The entity in question
/// </summary>
public NetEntity NetEntity;
/// <summary>
/// Location of the entity
/// </summary>
public NetCoordinates NetCoordinates;
/// <summary>
/// Populate the supermatter console nav map with a single entity
/// </summary>
public SupermatterNavMapData(NetEntity netEntity, NetCoordinates netCoordinates)
{
NetEntity = netEntity;
NetCoordinates = netCoordinates;
}
}
[Serializable, NetSerializable]
public struct SupermatterFocusData
{
/// <summary>
/// Focus entity
/// </summary>
public NetEntity NetEntity;
/// <summary>
/// The supermatter's integrity, from 0 to 100
/// </summary>
public float Integrity;
/// <summary>
/// The supermatter's power
/// </summary>
public float Power;
/// <summary>
/// The supermatter's emitted radiation
/// </summary>
public float Radiation;
/// <summary>
/// The supermatter's total absorbed moles
/// </summary>
public float AbsorbedMoles;
/// <summary>
/// The supermatter's temperature
/// </summary>
public float Temperature;
/// <summary>
/// The supermatter's temperature limit
/// </summary>
public float TemperatureLimit;
/// <summary>
/// The supermatter's waste multiplier
/// </summary>
public float WasteMultiplier;
/// <summary>
/// The supermatter's absorption ratio
/// </summary>
public float AbsorptionRatio;
/// <summary>
/// The supermatter's gas storage
/// </summary>
[DataField]
public Dictionary<Gas, float> GasStorage;
/// <summary>
/// Populates the supermatter console focus entry with supermatter data
/// </summary>
public SupermatterFocusData
(NetEntity netEntity,
float integrity,
float power,
float radiation,
float absorbedMoles,
float temperature,
float temperatureLimit,
float wasteMultiplier,
float absorptionRatio,
Dictionary<Gas, float> gasStorage)
{
NetEntity = netEntity;
Integrity = integrity;
Power = power;
Radiation = radiation;
AbsorbedMoles = absorbedMoles;
Temperature = temperature;
TemperatureLimit = temperatureLimit;
WasteMultiplier = wasteMultiplier;
AbsorptionRatio = absorptionRatio;
GasStorage = gasStorage;
}
}
[Serializable, NetSerializable]
public sealed class SupermatterConsoleBoundInterfaceState : BoundUserInterfaceState
{
/// <summary>
/// A list of all supermatters
/// </summary>
public SupermatterConsoleEntry[] Supermatters;
/// <summary>
/// Data for the UI focus (if applicable)
/// </summary>
public SupermatterFocusData? FocusData;
/// <summary>
/// Sends data from the server to the client to populate the atmos monitoring console UI
/// </summary>
public SupermatterConsoleBoundInterfaceState(SupermatterConsoleEntry[] supermatters, SupermatterFocusData? focusData)
{
Supermatters = supermatters;
FocusData = focusData;
}
}
[Serializable, NetSerializable]
public struct SupermatterConsoleEntry
{
/// <summary>
/// The entity in question
/// </summary>
public NetEntity NetEntity;
/// <summary>
/// Name of the entity
/// </summary>
public string EntityName;
/// <summary>
/// Current alert level
/// </summary>
public SupermatterStatusType EntityStatus;
/// <summary>
/// Used to populate the supermatter console UI with data from a single supermatter
/// </summary>
public SupermatterConsoleEntry
(NetEntity entity,
string entityName,
SupermatterStatusType status)
{
NetEntity = entity;
EntityName = entityName;
EntityStatus = status;
}
}
[Serializable, NetSerializable]
public sealed class SupermatterConsoleFocusChangeMessage : BoundUserInterfaceMessage
{
public NetEntity? FocusSupermatter;
/// <summary>
/// Used to inform the server that the specified focus for the atmos monitoring console has been changed by the client
/// </summary>
public SupermatterConsoleFocusChangeMessage(NetEntity? focusSupermatter)
{
FocusSupermatter = focusSupermatter;
}
}
[NetSerializable, Serializable]
public enum SupermatterConsoleVisuals
{
ComputerLayerScreen,
}
/// <summary>
/// UI key associated with the supermatter monitoring console
/// </summary>
[Serializable, NetSerializable]
public enum SupermatterConsoleUiKey
{
Key
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Serialization;
namespace Content.Shared._EE.Supermatter.Monitor;
[Serializable, NetSerializable]
public enum SupermatterStatusType : sbyte
{
Error = -1,
Inactive = 0,
Normal = 1,
Caution = 2,
Warning = 3,
Danger = 4,
Emergency = 5,
Delaminating = 6
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,37 @@
- files:
- 1.ogg
- 2.ogg
- 3.ogg
- 4.ogg
- 5.ogg
- 6.ogg
- 7.ogg
- 8.ogg
- 9.ogg
- 10.ogg
- 11.ogg
- 12.ogg
- 13.ogg
- 14.ogg
- 15.ogg
- 16.ogg
- 17.ogg
- 18.ogg
- 19.ogg
- 20.ogg
- 21.ogg
- 22.ogg
- 23.ogg
- 24.ogg
- 25.ogg
- 26.ogg
- 27.ogg
- 28.ogg
- 29.ogg
- 30.ogg
- 31.ogg
- 32.ogg
- 33.ogg
copyright: "Created by actioninja for tgstation13"
license: "CC-BY-SA-3.0"
source: "https://github.com/tgstation/tgstation/pull/45462"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,44 @@
supermatter-console-window-title = Supermatter Monitoring Console
supermatter-console-window-station-name = [color=white][font size=14]{$stationName}[/font][/color]
supermatter-console-window-unknown-location = Unknown location
supermatter-console-window-no-supermatters = [font size=16][color=white]No supermatter detected[/font]
supermatter-console-window-label-sm = {CAPITALIZE($name)}
supermatter-console-window-label-alert-types = Supermatter status:
supermatter-console-window-error-status = Error
supermatter-console-window-inactive-status = Inactive
supermatter-console-window-normal-status = Normal
supermatter-console-window-caution-status = Caution
supermatter-console-window-warning-status = Warning
supermatter-console-window-danger-status = Danger
supermatter-console-window-emergency-status = Emergency
supermatter-console-window-delaminating-status = Delaminating
supermatter-console-window-label-integrity = Integrity:
supermatter-console-window-label-integrity-bar = {$integrity}%
supermatter-console-window-label-power = Internal Energy:
supermatter-console-window-label-power-bar = {$power} {$prefix}eV
supermatter-console-window-label-radiation = Radiation Emission:
supermatter-console-window-label-radiation-bar = {$radiation} rads
supermatter-console-window-label-moles = Absorbed Moles:
supermatter-console-window-label-moles-bar = {$moles} Moles
supermatter-console-window-label-temperature = Temperature:
supermatter-console-window-label-temperature-limit = Temperature Limit:
supermatter-console-window-label-temperature-bar = {$temperature} K
supermatter-console-window-label-waste = Waste Multiplier:
supermatter-console-window-label-waste-bar = {$waste} x
supermatter-console-window-label-absorption = Absorption Ratio:
supermatter-console-window-label-absorption-bar = {$absorption}%
supermatter-console-window-label-gas = Unknown gas
supermatter-console-window-label-gas-bar = {$gas}%
supermatter-console-window-flavor-left = ⚠ Do not approach the crystal
supermatter-console-window-flavor-right = v1.1

View File

@@ -1,6 +1,7 @@
supermatter-announcer = Automatic Supermatter Engine
supermatter-examine-integrity =
Its' integrity is [color=yellow]{$integrity}%[/color].
Its integrity is [color=yellow]{$integrity}%[/color].
supermatter-healing =
Crystalline hyperstructure returning to safe operating parameters. Integrity: {$integrity}%.
supermatter-warning =
Warning! Crystal hyperstructure integrity faltering! Integrity: {$integrity}%.
supermatter-emergency =
@@ -14,9 +15,16 @@ supermatter-delam-tesla =
supermatter-delam-cascade =
CRYSTAL DELAMINATION IMMINENT! Harmonic frequency limits exceeded, casualty destabilization field could not be engaged!
supermatter-delam-cancel =
Crystalline hyperstructure returning to safe operating parameters. Failsafe has been Disengaged. Integrity: {$integrity}%.
Crystalline hyperstructure returning to safe operating parameters. Failsafe has been disengaged. Integrity: {$integrity}%.
supermatter-delam-player =
You feel reality distort for a moment...
supermatter-seconds-before-delam =
Estimated time before delamination: {$seconds} seconds.
supermatter-seconds-before-delam-countdown =
{$seconds} seconds remain before causality stabilization.
supermatter-seconds-before-delam-imminent =
{$seconds}...
supermatter-tamper-begin =
You begin carefully cutting a piece off the supermatter crystal...
supermatter-tamper-end =
@@ -24,3 +32,8 @@ supermatter-tamper-end =
supermatter-announcement-cc-tamper =
Our automatic casualty system has detected that the supermatter crystal structural integrity was compromised by an external force.
Engineering department, report to the supermatter engine immediately.
supermatter-collide =
{CAPITALIZE(THE($target))} slams into the {$sm}!
supermatter-collide-mob =
{CAPITALIZE(THE($target))}'s body flashes into dust!

View File

@@ -170,3 +170,6 @@ chat-speech-verb-electricity-2 = buzzes
chat-speech-verb-electricity-3 = screeches
chat-speech-verb-marish = Mars
chat-speech-verb-name-supermatter = Supermatter
chat-speech-verb-supermatter = states

View File

@@ -1,25 +0,0 @@
- type: entity
parent: BaseItem
id: SupermatterSliver
name: supermatter sliver
description: A shard from the station's Supermatter crystal. Highly radioactive.
components:
- type: PointLight
enabled: true
radius: 3
energy: 2
color: "#fff633"
- type: RadiationSource
intensity: .75
- type: Icon
sprite: Supermatter/supermatter_sliver.rsi
state: icon
- type: Sprite
sprite: Supermatter/supermatter_sliver.rsi
state: icon
- type: StealTarget
stealGroup: SupermatterSliver
- type: Tag
tags:
- HighRiskItem
- type: SupermatterImmune

Some files were not shown because too many files have changed in this diff Show More