Merge pull request #4625 from ShepardToTheStars/upstream-merge-august

Upstream Merge (August)
This commit is contained in:
Vanessa
2025-11-23 15:49:56 -06:00
committed by GitHub
2063 changed files with 45725 additions and 19057 deletions
@@ -0,0 +1,174 @@
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos.Components;
using Content.Shared.CCVar;
using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Benchmarks;
/// <summary>
/// Spawns N number of entities with a <see cref="DeltaPressureComponent"/> and
/// simulates them for a number of ticks M.
/// </summary>
[Virtual]
[GcServer(true)]
//[MemoryDiagnoser]
//[ThreadingDiagnoser]
public class DeltaPressureBenchmark
{
/// <summary>
/// Number of entities (windows, really) to spawn with a <see cref="DeltaPressureComponent"/>.
/// </summary>
[Params(1, 10, 100, 1000, 5000, 10000, 50000, 100000)]
public int EntityCount;
/// <summary>
/// Number of entities that each parallel processing job will handle.
/// </summary>
// [Params(1, 10, 100, 1000, 5000, 10000)] For testing how multithreading parameters affect performance (THESE TESTS TAKE 16+ HOURS TO RUN)
[Params(10)]
public int BatchSize;
/// <summary>
/// Number of entities to process per iteration in the DeltaPressure
/// processing loop.
/// </summary>
// [Params(100, 1000, 5000, 10000, 50000)]
[Params(1000)]
public int EntitiesPerIteration;
private readonly EntProtoId _windowProtoId = "Window";
private readonly EntProtoId _wallProtoId = "WallPlastitaniumIndestructible";
private TestPair _pair = default!;
private IEntityManager _entMan = default!;
private SharedMapSystem _map = default!;
private IRobustRandom _random = default!;
private IConfigurationManager _cvar = default!;
private ITileDefinitionManager _tileDefMan = default!;
private AtmosphereSystem _atmospereSystem = default!;
private Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>
_testEnt;
[GlobalSetup]
public async Task SetupAsync()
{
ProgramShared.PathOffset = "../../../../";
PoolManager.Startup();
_pair = await PoolManager.GetServerClient();
var server = _pair.Server;
var mapdata = await _pair.CreateTestMap();
_entMan = server.ResolveDependency<IEntityManager>();
_map = _entMan.System<SharedMapSystem>();
_random = server.ResolveDependency<IRobustRandom>();
_cvar = server.ResolveDependency<IConfigurationManager>();
_tileDefMan = server.ResolveDependency<ITileDefinitionManager>();
_atmospereSystem = _entMan.System<AtmosphereSystem>();
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
_cvar.SetCVar(CCVars.DeltaPressureParallelToProcessPerIteration, EntitiesPerIteration);
_cvar.SetCVar(CCVars.DeltaPressureParallelBatchSize, BatchSize);
var plating = _tileDefMan["Plating"].TileId;
/*
Basically, we want to have a 5-wide grid of tiles.
Edges are walled, and the length of the grid is determined by N + 2.
Windows should only touch the top and bottom walls, and each other.
*/
var length = EntityCount + 2; // ensures we can spawn exactly N windows between side walls
const int height = 5;
await server.WaitPost(() =>
{
// Fill required tiles (extend grid) with plating
for (var x = 0; x < length; x++)
{
for (var y = 0; y < height; y++)
{
_map.SetTile(mapdata.Grid, mapdata.Grid, new Vector2i(x, y), new Tile(plating));
}
}
// Spawn perimeter walls and windows row in the middle (y = 2)
const int midY = height / 2;
for (var x = 0; x < length; x++)
{
for (var y = 0; y < height; y++)
{
var coords = new EntityCoordinates(mapdata.Grid, x + 0.5f, y + 0.5f);
var isPerimeter = x == 0 || x == length - 1 || y == 0 || y == height - 1;
if (isPerimeter)
{
_entMan.SpawnEntity(_wallProtoId, coords);
continue;
}
// Spawn windows only on the middle row, spanning interior (excluding side walls)
if (y == midY)
{
_entMan.SpawnEntity(_windowProtoId, coords);
}
}
}
});
// Next we run the fixgridatmos command to ensure that we have some air on our grid.
// Wait a little bit as well.
// TODO: Unhardcode command magic string when fixgridatmos is an actual command we can ref and not just
// a stamp-on in AtmosphereSystem.
await _pair.WaitCommand("fixgridatmos " + mapdata.Grid.Owner, 1);
var uid = mapdata.Grid.Owner;
_testEnt = new Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>(
uid,
_entMan.GetComponent<GridAtmosphereComponent>(uid),
_entMan.GetComponent<GasTileOverlayComponent>(uid),
_entMan.GetComponent<MapGridComponent>(uid),
_entMan.GetComponent<TransformComponent>(uid));
}
[Benchmark]
public async Task PerformFullProcess()
{
await _pair.Server.WaitPost(() =>
{
while (!_atmospereSystem.RunProcessingStage(_testEnt, AtmosphereProcessingState.DeltaPressure)) { }
});
}
[Benchmark]
public async Task PerformSingleRunProcess()
{
await _pair.Server.WaitPost(() =>
{
_atmospereSystem.RunProcessingStage(_testEnt, AtmosphereProcessingState.DeltaPressure);
});
}
[GlobalCleanup]
public async Task CleanupAsync()
{
await _pair.DisposeAsync();
PoolManager.Shutdown();
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ public class MapLoadBenchmark
PoolManager.Shutdown();
}
public static readonly string[] MapsSource = { "Empty", "Box", "Aspid", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Gemini", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" };
public static string[] MapsSource { get; } = { "Empty", "Saltern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
[ParamsSource(nameof(MapsSource))]
public string Map;
-1
View File
@@ -6,7 +6,6 @@ using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Mind;
using Content.Server.Warps;
using Content.Shared.Warps;
using Robust.Shared;
using Robust.Shared.Analyzers;
@@ -21,7 +21,7 @@ namespace Content.Client.Actions.UI
/// </summary>
public (TimeSpan Start, TimeSpan End)? Cooldown { get; set; }
public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null, FormattedMessage? charges = null)
public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null)
{
_gameTiming = IoCManager.Resolve<IGameTiming>();
@@ -52,17 +52,6 @@ namespace Content.Client.Actions.UI
vbox.AddChild(description);
}
if (charges != null && !string.IsNullOrWhiteSpace(charges.ToString()))
{
var chargesLabel = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = { StyleNano.StyleClassTooltipActionCharges }
};
chargesLabel.SetMessage(charges);
vbox.AddChild(chargesLabel);
}
vbox.AddChild(_cooldownLabel = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
@@ -1,10 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}
@@ -15,6 +15,7 @@ namespace Content.Client.Administration.Managers
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IClientNetManager _netMgr = default!;
[Dependency] private readonly IClientConGroupController _conGroup = default!;
[Dependency] private readonly IClientConsoleHost _host = default!;
[Dependency] private readonly IResourceManager _res = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IUserInterfaceManager _userInterface = default!;
@@ -86,12 +87,12 @@ namespace Content.Client.Administration.Managers
private void UpdateMessageRx(MsgUpdateAdminStatus message)
{
_availableCommands.Clear();
var host = IoCManager.Resolve<IClientConsoleHost>();
// Anything marked as Any we'll just add even if the server doesn't know about it.
foreach (var (command, instance) in host.AvailableCommands)
foreach (var (command, instance) in _host.AvailableCommands)
{
if (Attribute.GetCustomAttribute(instance.GetType(), typeof(AnyCommandAttribute)) == null) continue;
if (Attribute.GetCustomAttribute(instance.GetType(), typeof(AnyCommandAttribute)) == null)
continue;
_availableCommands.Add(command);
}
@@ -1,7 +0,0 @@
using Content.Shared.Administration;
namespace Content.Client.Administration.Systems;
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
{
}
@@ -1,4 +1,4 @@
using Content.Client.Administration.Components;
using Content.Shared.Administration.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Administration.Systems;
@@ -1,33 +0,0 @@
using Content.Shared.Administration.Logs;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Administration.UI.CustomControls;
public sealed class AdminLogLabel : RichTextLabel
{
public AdminLogLabel(ref SharedAdminLog log, HSeparator separator)
{
Log = log;
Separator = separator;
SetMessage($"{log.Date:HH:mm:ss}: {log.Message}");
OnVisibilityChanged += VisibilityChanged;
}
public SharedAdminLog Log { get; }
public HSeparator Separator { get; }
private void VisibilityChanged(Control control)
{
Separator.Visible = Visible;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
OnVisibilityChanged -= VisibilityChanged;
}
}
@@ -1,16 +1,15 @@
using System.Linq;
using System.Text.RegularExpressions;
using Content.Client.Administration.Systems;
using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
@@ -96,13 +95,26 @@ public sealed partial class PlayerListControl : BoxContainer
private void FilterList()
{
_sortedPlayerList.Clear();
Regex filterRegex;
// There is no neat way to handle invalid regex being submitted other than
// catching and ignoring the exception which gets thrown when it's invalid.
try
{
filterRegex = new Regex(FilterLineEdit.Text, RegexOptions.IgnoreCase);
}
catch (ArgumentException)
{
return;
}
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
&& !filterRegex.IsMatch(displayName))
continue;
_sortedPlayerList.Add(info);
}
@@ -1,10 +1,8 @@
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
@@ -1,5 +1,6 @@
<Control xmlns="https://spacestation14.io"
xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls">
xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:ui="clr-namespace:Content.Client.Options.UI">
<PanelContainer StyleClasses="BackgroundDark">
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical">
@@ -56,6 +57,13 @@
<Control HorizontalExpand="True"/>
<Button Name="PopOutButton" Access="Public" Text="{Loc admin-logs-pop-out}"/>
</BoxContainer>
<BoxContainer HorizontalExpand="True">
<Button Name="RenderRichTextButton" Access="Public" Text="{Loc admin-logs-render-rich-text}"
StyleClasses="OpenRight" ToggleMode="True"/>
<Button Name="RemoveMarkupButton" Access="Public" Text="{Loc admin-logs-remove-markup}"
StyleClasses="OpenLeft" ToggleMode="True"/>
<Control HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<LineEdit Name="LogSearch" Access="Public" StyleClasses="actionSearchBox"
HorizontalExpand="true" PlaceHolder="{Loc admin-logs-search-logs-placeholder}"/>
@@ -1,6 +1,8 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.Administration.UI.Logs.Entries;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Robust.Client.AutoGenerated;
@@ -38,6 +40,9 @@ public sealed partial class AdminLogsControl : Control
SelectAllPlayersButton.OnPressed += SelectAllPlayers;
SelectNoPlayersButton.OnPressed += SelectNoPlayers;
RenderRichTextButton.OnPressed += RenderRichTextChanged;
RemoveMarkupButton.OnPressed += RemoveMarkupChanged;
RoundSpinBox.IsValid = i => i > 0 && i <= CurrentRound;
RoundSpinBox.ValueChanged += RoundSpinBoxChanged;
RoundSpinBox.InitDefaultButtons();
@@ -58,6 +63,8 @@ public sealed partial class AdminLogsControl : Control
private int CurrentRound { get; set; }
private Regex LogSearchRegex { get; set; } = new("");
public int SelectedRoundId => RoundSpinBox.Value;
public string Search => LogSearch.Text;
private int ShownLogs { get; set; }
@@ -77,6 +84,8 @@ public sealed partial class AdminLogsControl : Control
/// </summary>
private List<SharedAdminLog> RawAdminLogs { get; set; } = new();
private bool RenderRichText { get; set; }
private bool RemoveMarkup { get; set; }
public HashSet<LogType> SelectedTypes { get; } = new();
public HashSet<Guid> SelectedPlayers { get; } = new();
@@ -123,6 +132,19 @@ public sealed partial class AdminLogsControl : Control
private void LogSearchChanged(LineEditEventArgs args)
{
// This exception is thrown if the regex is invalid, which happens often, so we ignore it.
try
{
LogSearchRegex = new Regex(
"(" + LogSearch.Text + ")",
RegexOptions.IgnoreCase,
TimeSpan.FromSeconds(1));
}
catch (ArgumentException)
{
return;
}
UpdateLogs();
}
@@ -204,6 +226,26 @@ public sealed partial class AdminLogsControl : Control
UpdateLogs();
}
private void RenderRichTextChanged(ButtonEventArgs args)
{
RenderRichText = args.Button.Pressed;
RemoveMarkup = RemoveMarkup && !RenderRichText;
RemoveMarkupButton.Pressed = RemoveMarkup;
UpdateLogs();
}
private void RemoveMarkupChanged(ButtonEventArgs args)
{
RemoveMarkup = args.Button.Pressed;
RenderRichText = !RemoveMarkup && RenderRichText;
RenderRichTextButton.Pressed = RenderRichText;
UpdateLogs();
}
public void SetTypesSelection(HashSet<LogType> selectedTypes, bool invert = false)
{
SelectedTypes.Clear();
@@ -262,16 +304,15 @@ public sealed partial class AdminLogsControl : Control
foreach (var child in LogsContainer.Children)
{
if (child is not AdminLogLabel log)
{
if (child is not AdminLogEntry log)
continue;
}
child.Visible = ShouldShowLog(log);
if (child.Visible)
{
ShownLogs++;
}
if (!child.Visible)
continue;
log.RenderResults(LogSearchRegex, RenderRichText, RemoveMarkup);
ShownLogs++;
}
UpdateCount();
@@ -289,30 +330,30 @@ public sealed partial class AdminLogsControl : Control
button.Text.Contains(PlayerSearch.Text, StringComparison.OrdinalIgnoreCase);
}
private bool LogMatchesPlayerFilter(AdminLogLabel label)
private bool LogMatchesPlayerFilter(AdminLogEntry entry)
{
if (label.Log.Players.Length == 0)
if (entry.Log.Players.Length == 0)
return SelectedPlayers.Count == 0 || IncludeNonPlayerLogs;
return SelectedPlayers.Overlaps(label.Log.Players);
return SelectedPlayers.Overlaps(entry.Log.Players);
}
private bool ShouldShowLog(AdminLogLabel label)
private bool ShouldShowLog(AdminLogEntry entry)
{
// Check log type
if (!SelectedTypes.Contains(label.Log.Type))
if (!SelectedTypes.Contains(entry.Log.Type))
return false;
// Check players
if (!LogMatchesPlayerFilter(label))
if (!LogMatchesPlayerFilter(entry))
return false;
// Check impact
if (!SelectedImpacts.Contains(label.Log.Impact))
if (!SelectedImpacts.Contains(entry.Log.Impact))
return false;
// Check search
if (!label.Log.Message.Contains(LogSearch.Text, StringComparison.OrdinalIgnoreCase))
if (!LogSearchRegex.IsMatch(entry.Log.Message))
return false;
return true;
@@ -386,19 +427,31 @@ public sealed partial class AdminLogsControl : Control
{
var log = tempLogs[i];
ref var logRef = ref log; // It didn't like me doing this as one line lmao
var separator = new HSeparator();
var label = new AdminLogLabel(ref logRef, separator);
label.Visible = ShouldShowLog(label);
var entry = new AdminLogEntry(ref log);
//var label = new AdminLogLabel(ref logRef, separator);
//label.Visible = ShouldShowLog(label);
TotalLogs++;
if (label.Visible) ShownLogs++;
//if (label.Visible) ShownLogs++;
LogsContainer.AddChild(label);
LogsContainer.AddChild(separator);
//LogsContainer.AddChild(label);
LogsContainer.AddChild(entry);
}
UpdateCount();
UpdateLogs();
}
/*
var span = CollectionsMarshal.AsSpan(logs);
for (var i = 0; i < span.Length; i++)
{
ref var log = ref span[i];
var entry = new AdminLogEntry(ref log);
TotalLogs++;
LogsContainer.AddChild(entry);
}
UpdateLogs();
*/
// END DEN - Admin Log Sorting
private void ImpactButtonPressed(ButtonEventArgs args)
@@ -583,6 +636,7 @@ public sealed partial class AdminLogsControl : Control
SelectAllTypesButton.OnPressed -= SelectAllTypes;
SelectNoTypesButton.OnPressed -= SelectNoTypes;
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
SelectAllPlayersButton.OnPressed -= SelectAllPlayers;
SelectNoPlayersButton.OnPressed -= SelectNoPlayers;
@@ -1,4 +1,6 @@
using System.Linq;
using System.IO;
using System.Linq;
using Content.Client.Administration.UI.Logs.Entries;
using Content.Client.Eui;
using Content.Shared.Administration.Logs;
using Content.Shared.Eui;
@@ -0,0 +1,14 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
Orientation="Vertical">
<BoxContainer Margin="2">
<Collapsible>
<CollapsibleHeading Name="DetailsHeading" Access="Public">
<RichTextLabel Margin="20 0 0 0" Name="Message" MinSize="50 10" VerticalExpand="True" Access="Public" />
</CollapsibleHeading>
<CollapsibleBody Name="DetailsBody" Access="Public" />
</Collapsible>
</BoxContainer>
<cc:HSeparator/>
</BoxContainer>
@@ -0,0 +1,79 @@
using System.Text.RegularExpressions;
using Content.Client.Message;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.Logs.Entries;
[GenerateTypedNameReferences]
public sealed partial class AdminLogEntry : BoxContainer
{
private readonly IConfigurationManager _cfgManager;
public SharedAdminLog Log { get; }
private readonly string _rawMessage;
public AdminLogEntry(ref SharedAdminLog log)
{
_cfgManager = IoCManager.Resolve<IConfigurationManager>();
RobustXamlLoader.Load(this);
Log = log;
_rawMessage = $"{log.Date:HH:mm:ss}: {log.Message}";
Message.SetMessage(_rawMessage);
DetailsHeading.OnToggled += DetailsToggled;
}
/// <summary>
/// Sets text to be highlighted from a search result, and renders rich text, or removes all rich text markup.
/// </summary>
public void RenderResults(Regex highlightRegex, bool renderRichText, bool removeMarkup)
{
var color = _cfgManager.GetCVar(CCVars.AdminLogsHighlightColor);
var formattedMessage = renderRichText
? _rawMessage
: removeMarkup
? FormattedMessage.RemoveMarkupPermissive(_rawMessage)
: FormattedMessage.EscapeText(_rawMessage);
// Want to avoid highlighting smaller strings
if (highlightRegex.ToString().Length > 4)
{
try
{
formattedMessage = highlightRegex.Replace(formattedMessage, $"[color={color}]$1[/color]", 3);
}
catch (RegexMatchTimeoutException)
{
// if we time out then don't bother highlighting results
}
}
if (!FormattedMessage.TryFromMarkup(formattedMessage, out var outputMessage))
return;
Message.SetMessage(outputMessage);
}
/// <summary>
/// We perform some extra calculations in the dropdown, so we want to render that only when
/// the dropdown is actually opened.
/// This also removes itself from the event listener so it doesn't trigger again.
/// </summary>
private void DetailsToggled(BaseButton.ButtonToggledEventArgs args)
{
if (!args.Pressed || DetailsBody.ChildCount > 0)
return;
DetailsBody.AddChild(new AdminLogEntryDetails(Log));
DetailsHeading.OnToggled -= DetailsToggled;
}
}
@@ -0,0 +1,44 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
StyleClasses="BackgroundDark">
<BoxContainer Orientation="Vertical" Margin="4">
<BoxContainer Orientation="Vertical">
<Label Text="{Loc admin-logs-field-type}" Margin="0 0 4 0"/>
<Label Name="Type" Text="None" StyleClasses="LabelSecondaryColor" Access="Public"/>
</BoxContainer>
<cc:HSeparator/>
<BoxContainer Orientation="Vertical">
<Label Text="{Loc admin-logs-field-impact}" Margin="0 0 4 0"/>
<Label Name="Impact" Text="None" StyleClasses="LabelSecondaryColor" Access="Public"/>
</BoxContainer>
</BoxContainer>
<cc:VSeparator/>
<BoxContainer Orientation="Vertical" Margin="4">
<Label Text="{Loc admin-logs-field-time-header}"/>
<cc:HSeparator/>
<BoxContainer>
<Label Text="{Loc admin-logs-field-time-local}" Margin="0 0 4 0" HorizontalExpand="True"/>
<Label Name="LocalTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
</BoxContainer>
<cc:HSeparator/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc admin-logs-field-time-utc}" Margin="0 0 4 0" HorizontalExpand="True"/>
<Label Name="UTCTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
</BoxContainer>
<cc:HSeparator/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc admin-logs-field-time-round}" Margin="0 0 4 0" HorizontalExpand="True"/>
<Label Name="CurTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
<cc:VSeparator/>
<BoxContainer Orientation="Vertical" Margin="4" HorizontalExpand="True">
<Label Text="{Loc admin-logs-field-players-header}"/>
<cc:HSeparator/>
<BoxContainer Orientation="Horizontal" Margin="4" VerticalExpand="True">
<controls:ListContainer Name="PlayerListContainer" Access="Public" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
@@ -0,0 +1,98 @@
using System.Linq;
using Content.Client.Administration.Systems;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.Administration.Logs;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Network;
namespace Content.Client.Administration.UI.Logs.Entries;
[GenerateTypedNameReferences]
public sealed partial class AdminLogEntryDetails : BoxContainer
{
private readonly AdminSystem _adminSystem;
private readonly IUserInterfaceManager _uiManager;
private readonly IEntityManager _entManager;
public AdminLogEntryDetails(SharedAdminLog log)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
Type.Text = log.Type.ToString();
Impact.Text = log.Impact.ToString();
LocalTime.Text = $"{log.Date.ToLocalTime():HH:mm:ss}";
UTCTime.Text = $"{log.Date:HH:mm:ss}";
// TimeSpan and DateTime use different formatting string conventions for some completely logical reason
// that mere mortals such as myself will never be able to understand.
CurTime.Text = new TimeSpan(log.CurTime).ToString(@"hh\:mm\:ss");
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PopulateList(log.Players);
}
private void PopulateList(Guid[] players)
{
if (players.Length == 0)
return;
if (_adminSystem.PlayerList is not { } allPlayers || allPlayers.Count == 0)
return;
var listData = new List<PlayerListData>();
foreach (var playerGuid in players)
{
var netUserId = new NetUserId(playerGuid);
// Linq here is fine since this runs in response to admin input in the UI and
// this loop only tends to go through 1-4 iterations.
if (allPlayers.FirstOrDefault(player => player.SessionId == netUserId) is not { } playerInfo)
continue;
listData.Add(new PlayerListData(playerInfo));
}
if (listData.Count == 0)
return;
PlayerListContainer.PopulateList(listData);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (!(args.Function == EngineKeyFunctions.UIRightClick
|| args.Function == EngineKeyFunctions.UIClick)
|| selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
var entryLabel = new Label();
entryLabel.Text = $"{info.CharacterName} ({info.Username})";
var entry = new BoxContainer();
entry.AddChild(entryLabel);
button.AddChild(entry);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
}
@@ -57,12 +57,43 @@ public sealed partial class ObjectsTab : Control
private void TeleportTo(NetEntity nent)
{
_console.ExecuteCommand($"tpto {nent}");
var selection = _selections[ObjectTypeOptions.SelectedId];
switch (selection)
{
case ObjectsTabSelection.Grids:
{
// directly teleport to the entity
_console.ExecuteCommand($"tpto {nent}");
}
break;
case ObjectsTabSelection.Maps:
{
// teleport to the map, not to the map entity (which is in nullspace)
if (!_entityManager.TryGetEntity(nent, out var map) || !_entityManager.TryGetComponent<MapComponent>(map, out var mapComp))
break;
_console.ExecuteCommand($"tp 0 0 {mapComp.MapId}");
break;
}
case ObjectsTabSelection.Stations:
{
// teleport to the station's largest grid, not to the station entity (which is in nullspace)
if (!_entityManager.TryGetEntity(nent, out var station))
break;
var largestGrid = _entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetLargestGrid(station.Value);
if (largestGrid == null)
break;
_console.ExecuteCommand($"tpto {largestGrid.Value}");
break;
}
default:
throw new NotImplementedException();
}
}
private void Delete(NetEntity nent)
{
_console.ExecuteCommand($"delete {nent}");
RefreshObjectList();
}
public void RefreshObjectList()
@@ -76,28 +107,24 @@ public sealed partial class ObjectsTab : Control
switch (selection)
{
case ObjectsTabSelection.Stations:
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().Stations);
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetStationNames());
break;
case ObjectsTabSelection.Grids:
{
var query = _entityManager.AllEntityQueryEnumerator<MapGridComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
{
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
}
var query = _entityManager.AllEntityQueryEnumerator<MapGridComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
break;
}
break;
}
case ObjectsTabSelection.Maps:
{
var query = _entityManager.AllEntityQueryEnumerator<MapComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
{
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
}
var query = _entityManager.AllEntityQueryEnumerator<MapComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
break;
}
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
}
@@ -1,5 +1,6 @@
<PanelContainer xmlns="https://spacestation14.io"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Name="BackgroundColorPanel">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
@@ -20,7 +21,7 @@
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Button Name="DeleteButton"
<controls:ConfirmButton Name="DeleteButton"
Text="{Loc object-tab-entity-delete}"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"
@@ -52,7 +52,6 @@ public sealed partial class PlayerTab : Control
_config.OnValueChanged(CCVars.AdminPlayerTabColorSetting, ColorSettingChanged, true);
_config.OnValueChanged(CCVars.AdminPlayerTabSymbolSetting, SymbolSettingChanged, true);
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
@@ -65,7 +64,6 @@ public sealed partial class PlayerTab : Control
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
RefreshPlayerList(_adminSystem.PlayerList);
}
#region Antag Overlay
@@ -0,0 +1,5 @@
using Content.Shared.Animals.Systems;
namespace Content.Client.Animals.Systems;
public sealed class ParrotMemorySystem : SharedParrotMemorySystem;
@@ -1,6 +1,6 @@
using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Components;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects;
@@ -25,9 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<BodyComponent>(ent, out var body) &&
body.Prototype is not null &&
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
{
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
}
@@ -1,46 +1,11 @@
using Content.Client.Atmos.Components;
using Robust.Client.GameObjects;
using Content.Client.UserInterface.Systems.Storage.Controls;
using Content.Shared.Atmos.Piping;
using Content.Shared.Hands;
using Content.Shared.Atmos.Components;
using Content.Shared.Item;
namespace Content.Client.Atmos.EntitySystems;
public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent>
{
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PipeColorVisualsComponent, GetInhandVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<PipeColorVisualsComponent, BeforeRenderInGridEvent>(OnDrawInGrid);
}
/// <summary>
/// This method is used to display the color changes of the pipe on the screen..
/// </summary>
private void OnGetVisuals(Entity<PipeColorVisualsComponent> item, ref GetInhandVisualsEvent args)
{
foreach (var (_, layerData) in args.Layers)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
layerData.Color = pipeColor.Color;
}
}
/// <summary>
/// This method is used to change the pipe's color in a container grid.
/// </summary>
private void OnDrawInGrid(Entity<PipeColorVisualsComponent> item, ref BeforeRenderInGridEvent args)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
args.Color = pipeColor.Color;
}
protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args)
{
if (TryComp<SpriteComponent>(uid, out var sprite)
@@ -50,8 +15,6 @@ public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisual
var layer = sprite[PipeVisualLayers.Pipe];
layer.Color = color.WithAlpha(layer.Color.A);
}
_itemSystem.VisualsChanged(uid);
}
}
@@ -59,7 +59,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -72,7 +72,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPressureBound>())
{
_pressureCheck.AddItem(Loc.GetString($"{value}"), (int) value);
_pressureCheck.AddItem(Loc.GetString($"air-alarm-ui-pressure-bound-{value.ToString().ToLower()}"), (int) value);
}
_pressureCheck.SelectId((int) _data.PressureChecks);
@@ -27,9 +27,15 @@
</BoxContainer>
<!-- Lower row: every single gas -->
<Collapsible Margin="2 2 2 2">
<CollapsibleHeading Title="Gas filters" />
<CollapsibleHeading Title="{Loc 'air-alarm-ui-widget-gas-filters'}" />
<CollapsibleBody Margin="20 0 0 0">
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
<BoxContainer Orientation="Vertical">
<BoxContainer Margin="2">
<Button Name="CSelectAll" Text="{Loc 'air-alarm-ui-scrubber-select-all-gases-label'}" />
<Button Name="CDeselectAll" Text="{Loc 'air-alarm-ui-scrubber-deselect-all-gases-label'}" />
</BoxContainer>
<GridContainer Name="CGasContainer" Columns="3" />
</BoxContainer>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
@@ -1,23 +1,26 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems; // DeltaV
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class ScrubberControl : BoxContainer
{
[Dependency] private readonly IEntityManager _entMan = default!; // DeltaV
private readonly SharedAtmosphereSystem _atmos; // DeltaV
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private GasVentScrubberData _data;
private string _address;
public event Action<string, IAtmosDeviceData>? ScrubberDataChanged;
public event Action<IAtmosDeviceData>? ScrubberDataCopied;
public event Action<IAtmosDeviceData>? ScrubberDataCopied;
private CheckBox _enabled => CEnableDevice;
private CollapsibleHeading _addressLabel => CAddress;
@@ -25,15 +28,18 @@ public sealed partial class ScrubberControl : BoxContainer
private FloatSpinBox _volumeRate => CVolumeRate;
private CheckBox _wideNet => CWideNet;
private Button _copySettings => CCopySettings;
private Button _selectAll => CSelectAll;
private Button _deselectAll => CDeselectAll;
private GridContainer _gases => CGasContainer;
private Dictionary<Gas, Button> _gasControls = new();
public ScrubberControl(GasVentScrubberData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); // DeltaV
_atmos = _entMan.System<SharedAtmosphereSystem>(); // DeltaV
Name = address;
@@ -66,7 +72,7 @@ public sealed partial class ScrubberControl : BoxContainer
foreach (var value in Enum.GetValues<ScrubberPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -83,12 +89,28 @@ public sealed partial class ScrubberControl : BoxContainer
ScrubberDataCopied?.Invoke(_data);
};
foreach (var value in Enum.GetValues<Gas>())
var allGases = Enum.GetValues<Gas>();
_selectAll.OnPressed += _ =>
{
_data.FilterGases = new HashSet<Gas>(allGases);
ScrubberDataChanged?.Invoke(_address, _data);
};
_deselectAll.OnPressed += _ =>
{
_data.FilterGases = [];
ScrubberDataChanged?.Invoke(_address, _data);
};
foreach (var value in allGases)
{
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(value);
var gasName = _prototypeManager.Index(gasProtoId).Name;
var gasButton = new Button
{
Name = value.ToString(),
Text = Loc.GetString(_atmos.GetGas(value).Name), // DeltaV - localize it instead of showing the enum name
Text = Loc.GetString(gasName),
ToggleMode = true,
HorizontalExpand = true,
Pressed = _data.FilterGases.Contains(value)
@@ -1,19 +1,22 @@
using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems; // DeltaV
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Prototypes;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class SensorInfo : BoxContainer
{
[Dependency] private readonly IEntityManager _entMan = default!; // DeltaV
private readonly SharedAtmosphereSystem _atmos; // DeltaV
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate;
public event Action<AtmosSensorData>? SensorDataCopied;
private string _address;
@@ -26,9 +29,10 @@ public sealed partial class SensorInfo : BoxContainer
public SensorInfo(AtmosSensorData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); // DeltaV
_atmos = _entMan.System<SharedAtmosphereSystem>(); // DeltaV
_address = address;
@@ -50,8 +54,12 @@ public sealed partial class SensorInfo : BoxContainer
var label = new RichTextLabel();
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", Loc.GetString(_atmos.GetGas(gas).Name)), // DeltaV - localize it instead of showing the enum name
("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));
@@ -59,7 +67,7 @@ public sealed partial class SensorInfo : BoxContainer
_gasLabels.Add(gas, label);
var threshold = data.GasThresholds[gas];
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title"), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) =>
{
@@ -95,6 +103,9 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
@@ -117,8 +128,12 @@ public sealed partial class SensorInfo : BoxContainer
}
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", Loc.GetString(_atmos.GetGas(gas).Name)), // DeltaV - localize it instead of showing the enum name
("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));
@@ -2,6 +2,6 @@
HorizontalExpand="True" Orientation="Vertical"
Margin = "20 0 0 0" MinSize="160 0" >
<Label Name="CBoundLabel" HorizontalAlignment="Center" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'Enable'}" Pressed="True" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'air-alarm-ui-widget-enable'}" Pressed="True" />
<FloatSpinBox Name="CSpinner" />
</BoxContainer>
@@ -6,7 +6,7 @@
<CollapsibleBody Margin="20 0 0 0">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<CheckBox Name="CEnabled" Text="{Loc 'Enabled'}" />
<CheckBox Name="CEnabled" Text="{Loc 'air-alarm-ui-widget-enable'}" />
</BoxContainer>
<!-- Upper row: Danger bounds -->
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>
@@ -208,7 +208,7 @@ namespace Content.Client.Atmos.UI
});
presBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-pressure-val-text", ("pressure", $"{gasMix.Pressure:0.##}")),
Text = Loc.GetString("gas-analyzer-window-pressure-val-text", ("pressure", $"{gasMix.Pressure:0.00}")),
Align = Label.AlignMode.Right,
HorizontalExpand = true
});
@@ -232,8 +232,8 @@ namespace Content.Client.Atmos.UI
tempBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-temperature-val-text",
("tempK", $"{gasMix.Temperature:0.#}"),
("tempC", $"{TemperatureHelpers.KelvinToCelsius(gasMix.Temperature):0.#}")),
("tempK", $"{gasMix.Temperature:0.0}"),
("tempC", $"{TemperatureHelpers.KelvinToCelsius(gasMix.Temperature):0.0}")),
Align = Label.AlignMode.Right,
HorizontalExpand = true
});
+6 -8
View File
@@ -3,17 +3,13 @@ using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Linq;
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Shared.Audio.Effects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
@@ -31,6 +27,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -65,18 +62,19 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
get => _overlayEnabled;
set
{
if (_overlayEnabled == value) return;
if (_overlayEnabled == value)
return;
_overlayEnabled = value;
var overlayManager = IoCManager.Resolve<IOverlayManager>();
if (_overlayEnabled)
{
_overlay = new AmbientSoundOverlay(EntityManager, this, EntityManager.System<EntityLookupSystem>());
overlayManager.AddOverlay(_overlay);
_overlayManager.AddOverlay(_overlay);
}
else
{
overlayManager.RemoveOverlay(_overlay!);
_overlayManager.RemoveOverlay(_overlay!);
_overlay = null;
}
}
@@ -3,11 +3,8 @@ using Content.Client.Gameplay;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Random;
using Content.Shared.Random.Rules;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
@@ -25,6 +22,7 @@ public sealed partial class ContentAudioSystem
{
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -61,7 +59,7 @@ public sealed partial class ContentAudioSystem
private void InitializeAmbientMusic()
{
Subs.CVar(_configManager, CCVars.AmbientMusicVolume, AmbienceCVarChanged, true);
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("audio.ambience");
_sawmill = _logManager.GetSawmill("audio.ambience");
// Reset audio
_nextAudio = TimeSpan.MaxValue;
+1 -1
View File
@@ -125,7 +125,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
!buckled ||
args.Sprite == null)
{
//_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation); // WD EDIT
_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation);
return;
}
@@ -1,8 +1,8 @@
using System.Numerics;
using Content.Shared.Body.Components;
using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components;
using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components;
using Robust.Client.GameObjects;
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private EntityQuery<BodyComponent> _bodyQuery;
private EntityQuery<MobStateComponent> _mobStateQuery;
public override void Initialize()
{
base.Initialize();
_bodyQuery = GetEntityQuery<BodyComponent>();
_mobStateQuery = GetEntityQuery<MobStateComponent>();
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
}
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
continue;
// no effect for anything too exotic
if (!_bodyQuery.HasComp(mob))
// no effect for non-mobs that have MobMover, such as mechs and vehicles.
if (!_mobStateQuery.HasComp(mob))
continue;
var ent = Spawn(box.Effect, mapPos);
@@ -0,0 +1,30 @@
using Content.Shared.Changeling.Components;
using Content.Shared.Changeling.Systems;
using Robust.Client.GameObjects;
namespace Content.Client.Changeling.Systems;
public sealed class ChangelingIdentitySystem : SharedChangelingIdentitySystem
{
[Dependency] private readonly UserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ChangelingIdentityComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<ChangelingIdentityComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateUi(ent);
}
public void UpdateUi(EntityUid uid)
{
if (_ui.TryGetOpenUi(uid, ChangelingTransformUiKey.Key, out var bui))
{
bui.Update();
}
}
}
@@ -0,0 +1,35 @@
using Content.Shared.Changeling.Systems;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Changeling.UI;
[UsedImplicitly]
public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private ChangelingTransformMenu? _window;
protected override void Open()
{
base.Open();
_window = this.CreateWindow<ChangelingTransformMenu>();
_window.OnIdentitySelect += SendIdentitySelect;
_window.Update(Owner);
}
public override void Update()
{
if (_window == null)
return;
_window.Update(Owner);
}
public void SendIdentitySelect(NetEntity identityId)
{
SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId));
}
}
@@ -0,0 +1,8 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>
@@ -0,0 +1,62 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Changeling.UI;
[GenerateTypedNameReferences]
public sealed partial class ChangelingTransformMenu : RadialMenu
{
[Dependency] private readonly IEntityManager _entity = default!;
public event Action<NetEntity>? OnIdentitySelect;
public ChangelingTransformMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void Update(EntityUid uid)
{
Main.DisposeAllChildren();
if (!_entity.TryGetComponent<ChangelingIdentityComponent>(uid, out var identityComp))
return;
foreach (var identityUid in identityComp.ConsumedIdentities)
{
if (!_entity.TryGetComponent<MetaDataComponent>(identityUid, out var metadata))
continue;
var identityName = metadata.EntityName;
var button = new ChangelingTransformMenuButton()
{
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64, 64),
ToolTip = identityName,
};
var entView = new SpriteView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entView.SetEntity(identityUid);
button.OnButtonUp += _ =>
{
OnIdentitySelect?.Invoke(_entity.GetNetEntity(identityUid));
Close();
};
button.AddChild(entView);
Main.AddChild(button);
}
}
}
public sealed class ChangelingTransformMenuButton : RadialMenuTextureButtonWithSector;
@@ -2,7 +2,6 @@ using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.GameStates;
namespace Content.Client.Chemistry.EntitySystems;
@@ -11,6 +10,7 @@ public sealed class InjectorSystem : SharedInjectorSystem
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainers));
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainer));
}
}
@@ -38,13 +38,13 @@ public sealed class InjectorStatusControl : Control
// only updates the UI if any of the details are different than they previously were
if (PrevVolume == solution.Volume
&& PrevMaxVolume == solution.MaxVolume
&& PrevTransferAmount == _parent.Comp.TransferAmount
&& PrevTransferAmount == _parent.Comp.CurrentTransferAmount
&& PrevToggleState == _parent.Comp.ToggleState)
return;
PrevVolume = solution.Volume;
PrevMaxVolume = solution.MaxVolume;
PrevTransferAmount = _parent.Comp.TransferAmount;
PrevTransferAmount = _parent.Comp.CurrentTransferAmount;
PrevToggleState = _parent.Comp.ToggleState;
// Update current volume and injector state
@@ -59,6 +59,6 @@ public sealed class InjectorStatusControl : Control
("currentVolume", solution.Volume),
("totalVolume", solution.MaxVolume),
("modeString", modeStringLocalized),
("transferVolume", _parent.Comp.TransferAmount)));
("transferVolume", _parent.Comp.CurrentTransferAmount)));
}
}
+5
View File
@@ -0,0 +1,5 @@
using Content.Shared.Cloning;
namespace Content.Client.Cloning;
public sealed partial class CloningSystem : SharedCloningSystem;
@@ -1,13 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Client.DisplacementMap;
using Content.Client.Inventory;
using Content.Shared._DV.Silicon.IPC; // DeltaV - IPC Snouts
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.DisplacementMap;
using Content.Shared.Humanoid;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
@@ -15,7 +13,6 @@ using Content.Shared.Item;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
using Robust.Shared.Utility;
using static Robust.Client.GameObjects.SpriteComponent;
@@ -185,6 +182,7 @@ public sealed class ClientClothingSystem : ClothingSystem
var layer = new PrototypeLayerData();
layer.RsiPath = rsi.Path.ToString();
layer.State = state;
layer.Scale = clothing.Scale;
layers = new() { layer };
return true;
+8 -1
View File
@@ -83,7 +83,7 @@ public sealed partial class CreditsWindow : DefaultWindow
private async void PopulateAttributions(BoxContainer attributionsContainer, int count)
{
attributionsContainer.DisposeAllChildren();
attributionsContainer.RemoveAllChildren();
if (_attributions.Count == 0)
{
@@ -256,6 +256,8 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulateLicenses(BoxContainer licensesContainer)
{
licensesContainer.RemoveAllChildren();
foreach (var entry in CreditsManager.GetLicenses(_resourceManager).OrderBy(p => p.Name))
{
licensesContainer.AddChild(new Label
@@ -272,6 +274,8 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulatePatrons(BoxContainer patronsContainer)
{
patronsContainer.RemoveAllChildren();
var patrons = LoadPatrons();
// Do not show "become a patron" button on Steam builds
@@ -321,6 +325,8 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulateContributors(BoxContainer ss14ContributorsContainer)
{
ss14ContributorsContainer.RemoveAllChildren();
Button contributeButton;
ss14ContributorsContainer.AddChild(new BoxContainer
@@ -359,6 +365,7 @@ public sealed partial class CreditsWindow : DefaultWindow
AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt");
AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt");
AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt");
AddSection(Loc.GetString("credits-window-immortals-title"), "Immortals.txt", true);
AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true);
var linkGithub = _cfg.GetCVar(CCVars.InfoLinksGithub);
@@ -4,6 +4,7 @@ using Content.Shared.Damage.Systems;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Damage.Systems;
@@ -104,6 +105,8 @@ public sealed partial class StaminaSystem : SharedStaminaSystem
private void PlayAnimation(Entity<StaminaComponent, SpriteComponent> entity)
{
DebugTools.Assert(entity.Comp1.CritThreshold > entity.Comp1.AnimationThreshold, $"Animation threshold on {ToPrettyString(entity)} was not less than the crit threshold. This will cause errors, animation has been cancelled.");
var step = Math.Clamp((entity.Comp1.StaminaDamage - entity.Comp1.AnimationThreshold) /
(entity.Comp1.CritThreshold - entity.Comp1.AnimationThreshold),
0f,
+11 -8
View File
@@ -1,5 +1,6 @@
using Content.Shared.Drunk;
using Content.Shared.StatusEffect;
using Content.Shared.StatusEffectNew;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
@@ -43,19 +44,21 @@ public sealed class DrunkOverlay : Overlay
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<DrunkComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
var statusSys = _sysMan.GetEntitySystem<Shared.StatusEffectNew.StatusEffectsSystem>();
if (!statusSys.TryGetMaxTime<DrunkStatusEffectComponent>(playerEntity.Value, out var status))
return;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, SharedDrunkSystem.DrunkKey, out var time, status))
return;
var time = status.Item2;
var curTime = _timing.CurTime;
var timeLeft = (float) (time.Value.Item2 - curTime).TotalSeconds;
var power = SharedDrunkSystem.MagicNumber;
if (time != null)
{
var curTime = _timing.CurTime;
power = (float) (time - curTime).Value.TotalSeconds;
}
CurrentBoozePower += 8f * (0.5f*timeLeft - CurrentBoozePower) * args.DeltaSeconds / (timeLeft+1);
CurrentBoozePower += 8f * (power * 0.5f - CurrentBoozePower) * args.DeltaSeconds / (power+1);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
+20 -16
View File
@@ -1,4 +1,5 @@
using Content.Shared.Drunk;
using Content.Shared.StatusEffectNew;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
@@ -16,38 +17,41 @@ public sealed class DrunkSystem : SharedDrunkSystem
{
base.Initialize();
SubscribeLocalEvent<DrunkComponent, ComponentInit>(OnDrunkInit);
SubscribeLocalEvent<DrunkComponent, ComponentShutdown>(OnDrunkShutdown);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusApplied);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRemovedEvent>(OnStatusRemoved);
SubscribeLocalEvent<DrunkComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<DrunkComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerAttachedEvent>>(OnPlayerAttached);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerDetachedEvent>>(OnPlayerDetached);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, DrunkComponent component, LocalPlayerAttachedEvent args)
private void OnStatusApplied(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectAppliedEvent args)
{
_overlayMan.AddOverlay(_overlay);
if (!_overlayMan.HasOverlay<DrunkOverlay>())
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, DrunkComponent component, LocalPlayerDetachedEvent args)
private void OnStatusRemoved(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRemovedEvent args)
{
if (Status.HasEffectComp<DrunkStatusEffectComponent>(args.Target))
return;
if (_player.LocalEntity != args.Target)
return;
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnDrunkInit(EntityUid uid, DrunkComponent component, ComponentInit args)
private void OnPlayerAttached(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRelayedEvent<LocalPlayerAttachedEvent> args)
{
if (_player.LocalEntity == uid)
_overlayMan.AddOverlay(_overlay);
_overlayMan.AddOverlay(_overlay);
}
private void OnDrunkShutdown(EntityUid uid, DrunkComponent component, ComponentShutdown args)
private void OnPlayerDetached(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRelayedEvent<LocalPlayerDetachedEvent> args)
{
if (_player.LocalEntity == uid)
{
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
@@ -1,7 +0,0 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion;
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
{
}
@@ -1,7 +0,0 @@
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
namespace Content.Client.Explosion;
[RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent {}
-10
View File
@@ -1,10 +0,0 @@
namespace Content.Client.Explosion;
public sealed partial class TriggerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
InitializeProximity();
}
}
@@ -0,0 +1,5 @@
using Content.Shared.Forensics.Systems;
namespace Content.Client.Forensics.Systems;
public sealed class ForensicsSystem : SharedForensicsSystem;
@@ -34,6 +34,8 @@ namespace Content.Client.GameTicking.Managers
[ViewVariables] public TimeSpan StartTime { get; private set; }
[ViewVariables] public new bool Paused { get; private set; }
public override IReadOnlyList<(TimeSpan, string)> AllPreviousGameRules => new List<(TimeSpan, string)>();
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> JobsAvailable => _jobsAvailable;
[ViewVariables] public IReadOnlyDictionary<NetEntity, string> StationNames => _stationNames;
+5 -1
View File
@@ -1,5 +1,6 @@
using Content.Shared.HotPotato;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.HotPotato;
@@ -10,6 +11,9 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private readonly EntProtoId _hotPotatoEffectId = "HotPotatoEffect";
// TODO: particle system
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -23,7 +27,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
if (_timing.CurTime < comp.TargetTime)
continue;
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
Spawn("HotPotatoEffect", _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
Spawn(_hotPotatoEffectId, _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
}
}
}
@@ -23,6 +23,8 @@ public sealed class ImplanterSystem : SharedImplanterSystem
{
if (_uiSystem.TryGetOpenUi<DeimplantBoundUserInterface>(uid, DeimplantUiKey.Key, out var bui))
{
// TODO: Don't use protoId for deimplanting
// and especially not raw strings!
Dictionary<string, string> implants = new();
foreach (var implant in component.DeimplantWhitelist)
{
@@ -0,0 +1,5 @@
using Content.Shared.Implants;
namespace Content.Client.Implants;
public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem;
+1 -1
View File
@@ -34,7 +34,6 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
common.AddFunction(ContentKeyFunctions.SaveItemLocation);
common.AddFunction(ContentKeyFunctions.Point);
common.AddFunction(ContentKeyFunctions.ToggleStanding); // WD EDIT
common.AddFunction(ContentKeyFunctions.ZoomOut);
common.AddFunction(ContentKeyFunctions.ZoomIn);
common.AddFunction(ContentKeyFunctions.ResetZoom);
@@ -63,6 +62,7 @@ namespace Content.Client.Input
human.AddFunction(EngineKeyFunctions.MoveLeft);
human.AddFunction(EngineKeyFunctions.MoveRight);
human.AddFunction(EngineKeyFunctions.Walk);
human.AddFunction(ContentKeyFunctions.ToggleKnockdown);
human.AddFunction(ContentKeyFunctions.SwapHands);
human.AddFunction(ContentKeyFunctions.SwapHandsReverse);
human.AddFunction(ContentKeyFunctions.Drop);
@@ -294,7 +294,6 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
SetMaster(uid, null);
TrySetChannels(uid, data);
instrument.MidiEventBuffer.Clear();
instrument.Renderer.OnMidiEvent += instrument.MidiEventBuffer.Add;
return true;
}
@@ -22,7 +22,6 @@ namespace Content.Client.Inventory
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ClientClothingSystem _clothingVisualsSystem = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
@@ -116,6 +115,13 @@ namespace Content.Client.Inventory
OnLinkInventorySlots?.Invoke(uid, component);
}
protected override void OnInit(Entity<InventoryComponent> ent, ref ComponentInit args)
{
base.OnInit(ent, ref args);
_clothingVisualsSystem.InitClothing(ent.Owner, ent.Comp);
}
public override void Shutdown()
{
CommandBinds.Unregister<ClientInventorySystem>();
@@ -262,7 +268,6 @@ namespace Content.Client.Inventory
TryAddSlotData((ent.Owner, inventorySlots), (SlotData)slot);
}
_clothingVisualsSystem.InitClothing(ent, ent.Comp);
if (ent.Owner == _playerManager.LocalEntity)
ReloadInventory(inventorySlots);
}
@@ -1,8 +0,0 @@
using Content.Shared.Kitchen;
namespace Content.Client.Kitchen;
public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
{
}
@@ -33,6 +33,10 @@ namespace Content.Client.Lathe.UI
};
_menu.OnClaimMiningPoints += () => SendMessage(new LatheClaimMiningPointsMessage()); // DeltaV
_menu.QueueDeleteAction += index => SendMessage(new LatheDeleteRequestMessage(index));
_menu.QueueMoveUpAction += index => SendMessage(new LatheMoveRequestMessage(index, -1));
_menu.QueueMoveDownAction += index => SendMessage(new LatheMoveRequestMessage(index, 1));
_menu.DeleteFabricatingAction += () => SendMessage(new LatheAbortFabricationMessage());
}
protected override void UpdateState(BoundUserInterfaceState state)
+13
View File
@@ -1,6 +1,7 @@
<DefaultWindow
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:ui="clr-namespace:Content.Client.Materials.UI"
Title="{Loc 'lathe-menu-title'}"
MinSize="550 450"
@@ -110,6 +111,18 @@
HorizontalAlignment="Left"
Margin="130 0 0 0">
</Label>
<Button
Name="DeleteFabricating"
Margin="0"
Text="✖"
SetSize="38 32"
HorizontalAlignment="Right"
ToolTip="{Loc 'lathe-menu-delete-fabricating-tooltip'}">
<Button.StyleClasses>
<system:String>Caution</system:String>
<system:String>OpenLeft</system:String>
</Button.StyleClasses>
</Button>
</PanelContainer>
</BoxContainer>
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
+27 -9
View File
@@ -33,6 +33,10 @@ public sealed partial class LatheMenu : DefaultWindow
public event Action<BaseButton.ButtonEventArgs>? OnServerListButtonPressed;
public event Action<string, int>? RecipeQueueAction;
public event Action? OnClaimMiningPoints; // DeltaV
public event Action<int>? QueueDeleteAction;
public event Action<int>? QueueMoveUpAction;
public event Action<int>? QueueMoveDownAction;
public event Action? DeleteFabricatingAction;
public List<ProtoId<LatheRecipePrototype>> Recipes = new();
@@ -60,12 +64,21 @@ public sealed partial class LatheMenu : DefaultWindow
};
AmountLineEdit.OnTextChanged += _ =>
{
if (int.TryParse(AmountLineEdit.Text, out var amount))
{
if (amount > LatheSystem.MaxItemsPerRequest)
AmountLineEdit.Text = LatheSystem.MaxItemsPerRequest.ToString();
else if (amount < 0)
AmountLineEdit.Text = "0";
}
PopulateRecipes();
};
FilterOption.OnItemSelected += OnItemSelected;
ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a);
DeleteFabricating.OnPressed += _ => DeleteFabricatingAction?.Invoke();
}
public void SetEntity(EntityUid uid)
@@ -266,22 +279,27 @@ public sealed partial class LatheMenu : DefaultWindow
/// Populates the build queue list with all queued items
/// </summary>
/// <param name="queue"></param>
public void PopulateQueueList(IReadOnlyCollection<ProtoId<LatheRecipePrototype>> queue)
public void PopulateQueueList(IReadOnlyCollection<LatheRecipeBatch> queue)
{
QueueList.DisposeAllChildren();
var idx = 1;
foreach (var recipeProto in queue)
foreach (var batch in queue)
{
var recipe = _prototypeManager.Index(recipeProto);
var queuedRecipeBox = new BoxContainer();
queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
var recipe = _prototypeManager.Index(batch.Recipe);
queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe));
var itemName = _lathe.GetRecipeName(batch.Recipe);
string displayText;
if (batch.ItemsRequested > 1)
displayText = Loc.GetString("lathe-menu-item-batch", ("index", idx), ("name", itemName), ("printed", batch.ItemsPrinted), ("total", batch.ItemsRequested));
else
displayText = Loc.GetString("lathe-menu-item-single", ("index", idx), ("name", itemName));
var queuedRecipeBox = new QueuedRecipeControl(displayText, idx - 1, GetRecipeDisplayControl(recipe));
queuedRecipeBox.OnDeletePressed += s => QueueDeleteAction?.Invoke(s);
queuedRecipeBox.OnMoveUpPressed += s => QueueMoveUpAction?.Invoke(s);
queuedRecipeBox.OnMoveDownPressed += s => QueueMoveDownAction?.Invoke(s);
var queuedRecipeLabel = new Label();
queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}";
queuedRecipeBox.AddChild(queuedRecipeLabel);
QueueList.AddChild(queuedRecipeBox);
idx++;
}
@@ -0,0 +1,35 @@
<Control xmlns="https://spacestation14.io"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<BoxContainer Orientation="Horizontal">
<BoxContainer
Name="RecipeDisplayContainer"
Margin="0 0 4 0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MinSize="32 32"
/>
<Label Name="RecipeName" HorizontalExpand="True" />
<Button
Name="MoveUp"
Margin="0"
Text="⏶"
StyleClasses="OpenRight"
ToolTip="{Loc 'lathe-menu-move-up-tooltip'}"/>
<Button
Name="MoveDown"
Margin="0"
Text="⏷"
StyleClasses="OpenBoth"
ToolTip="{Loc 'lathe-menu-move-down-tooltip'}"/>
<Button
Name="Delete"
Margin="0"
Text="✖"
ToolTip="{Loc 'lathe-menu-delete-item-tooltip'}">
<Button.StyleClasses>
<system:String>Caution</system:String>
<system:String>OpenLeft</system:String>
</Button.StyleClasses>
</Button>
</BoxContainer>
</Control>
@@ -0,0 +1,36 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Lathe.UI;
[GenerateTypedNameReferences]
public sealed partial class QueuedRecipeControl : Control
{
public Action<int>? OnDeletePressed;
public Action<int>? OnMoveUpPressed;
public Action<int>? OnMoveDownPressed;
public QueuedRecipeControl(string displayText, int index, Control displayControl)
{
RobustXamlLoader.Load(this);
RecipeName.Text = displayText;
RecipeDisplayContainer.AddChild(displayControl);
MoveUp.OnPressed += (_) =>
{
OnMoveUpPressed?.Invoke(index);
};
MoveDown.OnPressed += (_) =>
{
OnMoveDownPressed?.Invoke(index);
};
Delete.OnPressed += (_) =>
{
OnDeletePressed?.Invoke(index);
};
}
}
@@ -1,36 +1,46 @@
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Robust.Client.GameObjects;
namespace Content.Client.Light.Visualizers;
namespace Content.Client.Light.EntitySystems;
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
public sealed class LightBulbSystem : SharedLightBulbSystem
{
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightBulbComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// update sprite state
if (AppearanceSystem.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
if (_appearance.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
{
switch (state)
{
case LightBulbState.Normal:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break;
case LightBulbState.Broken:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break;
case LightBulbState.Burned:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break;
}
}
// also update sprites color
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
if (_appearance.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{
SpriteSystem.SetColor((uid, args.Sprite), color);
_sprite.SetColor((uid, args.Sprite), color);
}
}
}
@@ -0,0 +1,5 @@
using Content.Shared.Light.EntitySystems;
namespace Content.Client.Light.EntitySystems;
public sealed class PoweredLightSystem : SharedPoweredLightSystem;
@@ -72,6 +72,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
});
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor());
_configurationManager.OnValueChanged(CCVars.GameRoleLoadoutTimers, _ => RefreshProfileEditor());
_configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor());
}
@@ -1093,7 +1093,7 @@ namespace Content.Client.Lobby.UI
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
{
Title = jobProto?.ID + "-loadout",
Title = Loc.GetString("loadout-window-title-loadout", ("job", $"{jobProto?.LocalizedName}")),
};
// Refresh the buttons etc.
@@ -1,7 +1,5 @@
using System.Numerics;
using Content.Shared.Emag.Systems;
using Content.Shared.Medical.Cryogenics;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
namespace Content.Client.Medical.Cryogenics;
@@ -15,11 +13,6 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
{
base.Initialize();
SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentRemove>(OnCryoPodRemoval);
@@ -53,8 +46,8 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
return;
}
if (!_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, out var isOpen, args.Component)
|| !_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.IsOn, out var isOn, args.Component))
if (!_appearance.TryGetData<bool>(uid, CryoPodVisuals.ContainsEntity, out var isOpen, args.Component)
|| !_appearance.TryGetData<bool>(uid, CryoPodVisuals.IsOn, out var isOn, args.Component))
{
return;
}
@@ -0,0 +1,5 @@
using Content.Shared.Medical.SuitSensors;
namespace Content.Client.Medical.SuitSensors;
public sealed class SuitSensorSystem : SharedSuitSensorSystem;
@@ -0,0 +1,5 @@
using Content.Shared.Morgue;
namespace Content.Client.Morgue;
public sealed class CrematoriumSystem : SharedCrematoriumSystem;
+5
View File
@@ -0,0 +1,5 @@
using Content.Shared.Morgue;
namespace Content.Client.Morgue;
public sealed class MorgueSystem : SharedMorgueSystem;
@@ -1,7 +1,6 @@
using System.Numerics;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Client.GameObjects;
using Robust.Client.Player;
namespace Content.Client.Movement.Systems;
@@ -63,4 +62,15 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
UpdateEyeOffset((entity, eyeComponent));
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// TODO: Ideally we wouldn't want this to run in both FrameUpdate and Update, but we kind of have to since the visual update happens in FrameUpdate, but interaction update happens in Update. It's a workaround and a better solution should be found.
var eyeEntities = AllEntityQuery<ContentEyeComponent, EyeComponent>();
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
{
UpdateEyeOffset((entity, eyeComponent));
}
}
}
@@ -1,10 +1,10 @@
using System.Numerics;
using Content.Shared.Movement.Components; // DeltaV - make EyeCursorOffsetComponent entirely Shared
using Content.Client.Viewport;
using Content.Shared.Camera;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Shared.Map;
using Robust.Client.Player;
namespace Content.Client.Movement.Systems;
@@ -12,13 +12,10 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IClyde _clyde = default!;
// This value is here to make sure the user doesn't have to move their mouse
// all the way out to the edge of the screen to get the full offset.
static private float _edgeOffset = 0.9f;
private static float _edgeOffset = 0.8f;
public override void Initialize()
{
@@ -46,25 +43,29 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem
public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component)
{
var localPlayer = _player.LocalEntity;
var mousePos = _inputManager.MouseScreenPosition;
var screenSize = _clyde.MainWindow.Size;
var minValue = MathF.Min(screenSize.X / 2, screenSize.Y / 2) * _edgeOffset;
var mouseNormalizedPos = new Vector2(-(mousePos.X - screenSize.X / 2) / minValue, (mousePos.Y - screenSize.Y / 2) / minValue); // X needs to be inverted here for some reason, otherwise it ends up flipped.
if (localPlayer == null)
// We need the main viewport where the game content is displayed, as certain UI layouts (e.g. Separated HUD) can make it a different size to the game window.
if (_eyeManager.MainViewport is not ScalingViewport vp)
return null;
var playerPos = _transform.GetWorldPosition(localPlayer.Value);
var mousePos = _inputManager.MouseScreenPosition.Position; // TODO: If we ever get a right-aligned Separated HUD setting, this might need to be adjusted for that.
var viewportSize = vp.PixelSize; // The size of the game viewport, including black bars - does not include the chatbox in Separated HUD view.
var scalingViewportSize = vp.ViewportSize * vp.CurrentRenderScale; // The size of the viewport in which the game is rendered (i.e. not including black bars). Note! Can extend outside the game window with certain zoom settings!
var visibleViewportSize = Vector2.Min(viewportSize, scalingViewportSize); // The size of the game viewport that is "actually visible" to the player, cutting off over-extensions and not counting black bar padding.
Matrix3x2.Invert(_eyeManager.MainViewport.GetLocalToScreenMatrix(), out var matrix);
var mouseCoords = Vector2.Transform(mousePos, matrix); // Gives the mouse position inside of the *scaling viewport*, i.e. 0,0 is inside the black bars. Note! 0,0 can be outside the game window with certain zoom settings!
var boundedMousePos = Vector2.Clamp(Vector2.Min(mouseCoords, mousePos), Vector2.Zero, visibleViewportSize); // Mouse position inside the visible game viewport's bounds.
var offsetRadius = MathF.Min(visibleViewportSize.X / 2f, visibleViewportSize.Y / 2f) * _edgeOffset;
var mouseNormalizedPos = new Vector2(-(boundedMousePos.X - visibleViewportSize.X / 2f) / offsetRadius, (boundedMousePos.Y - visibleViewportSize.Y / 2f) / offsetRadius);
if (component == null)
{
component = EnsureComp<EyeCursorOffsetComponent>(uid);
}
// Doesn't move the offset if the mouse has left the game window!
if (mousePos.Window != WindowId.Invalid)
if (_inputManager.MouseScreenPosition.Window != WindowId.Invalid)
{
// The offset must account for the in-world rotation.
var eyeRotation = _eyeManager.CurrentEye.Rotation;
@@ -85,7 +86,7 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem
Vector2 vectorOffset = component.TargetPosition - component.CurrentPosition;
if (vectorOffset.Length() > component.OffsetSpeed)
{
vectorOffset = vectorOffset.Normalized() * component.OffsetSpeed;
vectorOffset = vectorOffset.Normalized() * component.OffsetSpeed; // TODO: Probably needs to properly account for time delta or something.
}
component.CurrentPosition += vectorOffset;
}
+4 -5
View File
@@ -20,6 +20,7 @@ namespace Content.Client.NPC
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IResourceCache _cache = default!;
[Dependency] private readonly NPCSteeringSystem _steering = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
@@ -30,17 +31,15 @@ namespace Content.Client.NPC
get => _modes;
set
{
var overlayManager = IoCManager.Resolve<IOverlayManager>();
if (value == PathfindingDebugMode.None)
{
Breadcrumbs.Clear();
Polys.Clear();
overlayManager.RemoveOverlay<PathfindingOverlay>();
_overlayManager.RemoveOverlay<PathfindingOverlay>();
}
else if (!overlayManager.HasOverlay<PathfindingOverlay>())
else if (!_overlayManager.HasOverlay<PathfindingOverlay>())
{
overlayManager.AddOverlay(new PathfindingOverlay(EntityManager, _eyeManager, _inputManager, _mapManager, _cache, this, _mapSystem, _transformSystem));
_overlayManager.AddOverlay(new PathfindingOverlay(EntityManager, _eyeManager, _inputManager, _mapManager, _cache, this, _mapSystem, _transformSystem));
}
if ((value & PathfindingDebugMode.Steering) != 0x0)
@@ -5,15 +5,15 @@
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
<Button Name="Set" Text="Set" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Add" Text="Add" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Set" Text="{Loc 'network-configurator-text-set'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Add" Text="{Loc 'network-configurator-text-add'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<!-- Edit might not be needed -->
<!--<Button Name="Edit" Text="Edit" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-edit'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>-->
<Button Name="Clear" Text="Clear" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
<Button Name="Clear" Text="{Loc 'network-configurator-text-clear'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 0 8 8">
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Copy" Text="{Loc 'network-configurator-text-copy'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="{Loc 'network-configurator-text-show'}" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer>
<Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer>
@@ -1,7 +1,8 @@
using Content.Client.Options.UI;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
namespace Content.Client._DV.Options.UI;
namespace Content.Client.Options.UI;
/// <summary>
/// Standard UI control used for color sliders in the options menu. Intended for use with <see cref="OptionsTabControlRow"/>.
@@ -121,6 +121,19 @@ public sealed partial class OptionsTabControlRow : Control
return AddOption(new OptionSliderFloatCVar(this, _cfg, cVar, slider, min, max, scale, FormatPercent));
}
/// <summary>
/// Add a color slider option, backed by a simple string CVar.
/// </summary>
/// <param name="cVar">The CVar represented by the slider.</param>
/// <param name="slider">The UI control for the option.</param>
/// <returns>The option instance backing the added option.</returns>
public OptionColorSliderCVar AddOptionColorSlider(
CVarDef<string> cVar,
OptionColorSlider slider)
{
return AddOption(new OptionColorSliderCVar(this, _cfg, cVar, slider));
}
/// <summary>
/// Add a slider option, backed by a simple integer CVar.
/// </summary>
@@ -518,6 +531,58 @@ public sealed class OptionSliderFloatCVar : BaseOptionCVar<float>
}
}
/// <summary>
/// Implementation of a CVar option that simply corresponds with a string <see cref="OptionColorSlider"/>.
/// </summary>
/// <seealso cref="OptionsTabControlRow"/>
public sealed class OptionColorSliderCVar : BaseOptionCVar<string>
{
private readonly OptionColorSlider _slider;
protected override string Value
{
get => _slider.Slider.Color.ToHex();
set
{
_slider.Slider.Color = Color.FromHex(value);
UpdateLabelColor();
}
}
/// <summary>
/// Creates a new instance of this type.
/// </summary>
/// <remarks>
/// <para>
/// It is generally more convenient to call overloads on <see cref="OptionsTabControlRow"/>
/// such as <see cref="OptionsTabControlRow.AddOptionPercentSlider"/> instead of instantiating this type directly.
/// </para>
/// </remarks>
/// <param name="controller">The control row that owns this option.</param>
/// <param name="cfg">The configuration manager to get and set values from.</param>
/// <param name="cVar">The CVar that is being controlled by this option.</param>
/// <param name="slider">The UI control for the option.</param>
public OptionColorSliderCVar(
OptionsTabControlRow controller,
IConfigurationManager cfg,
CVarDef<string> cVar,
OptionColorSlider slider) : base(controller, cfg, cVar)
{
_slider = slider;
slider.Slider.OnColorChanged += _ =>
{
ValueChanged();
UpdateLabelColor();
};
}
private void UpdateLabelColor()
{
_slider.ExampleLabel.FontColorOverride = Color.FromHex(Value);
}
}
/// <summary>
/// Implementation of a CVar option that simply corresponds with an integer <see cref="OptionSlider"/>.
/// </summary>
@@ -14,6 +14,10 @@
<ui:OptionSlider Name="SpeechBubbleTextOpacitySlider" Title="{Loc 'ui-options-speech-bubble-text-opacity'}" />
<ui:OptionSlider Name="SpeechBubbleSpeakerOpacitySlider" Title="{Loc 'ui-options-speech-bubble-speaker-opacity'}" />
<ui:OptionSlider Name="SpeechBubbleBackgroundOpacitySlider" Title="{Loc 'ui-options-speech-bubble-background-opacity'}" />
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
<ui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
Example="{Loc 'ui-options-highlights-color-example'}"/>
<Label Text="{Loc 'ui-options-accessability-header-content'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="CensorNudityCheckBox" Text="{Loc 'ui-options-censor-nudity'}" />
@@ -20,6 +20,8 @@ public sealed partial class AccessibilityTab : Control
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
Control.AddOptionPercentSlider(CCVars.SpeechBubbleSpeakerOpacity, SpeechBubbleSpeakerOpacitySlider);
Control.AddOptionPercentSlider(CCVars.SpeechBubbleBackgroundOpacity, SpeechBubbleBackgroundOpacitySlider);
Control.AddOptionCheckBox(CCVars.ChatAutoFillHighlights, AutoFillHighlightsCheckBox);
Control.AddOptionColorSlider(CCVars.ChatHighlightsColor, HighlightsColorSlider);
Control.AddOptionCheckBox(CCVars.AccessibilityClientCensorNudity, CensorNudityCheckBox);
@@ -9,6 +9,9 @@
<ui:OptionDropDown Name="DropDownPlayerTabSymbolSetting" Title="{Loc 'ui-options-admin-player-tab-symbol-setting'}" />
<ui:OptionDropDown Name="DropDownPlayerTabRoleSetting" Title="{Loc 'ui-options-admin-player-tab-role-setting'}" />
<ui:OptionDropDown Name="DropDownPlayerTabColorSetting" Title="{Loc 'ui-options-admin-player-tab-color-setting'}" />
<Label Text="{Loc 'ui-options-admin-logs-title'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="ColorSliderLogsHighlight" Title="{Loc 'ui-options-admin-logs-highlight-color'}" />
<Label Text="{Loc 'ui-options-admin-overlay-title'}"
StyleClasses="LabelKeyText"/>
<ui:OptionDropDown Name="DropDownOverlayAntagFormat" Title="{Loc 'ui-options-admin-overlay-antag-format'}" />
@@ -51,6 +51,8 @@ public sealed partial class AdminOptionsTab : Control
playerTabSymbolSettings.Add(new OptionDropDownCVar<string>.ValueOption(setting.ToString()!, Loc.GetString($"ui-options-admin-player-tab-symbol-setting-{setting.ToString()!.ToLower()}")));
}
Control.AddOptionColorSlider(CCVars.AdminLogsHighlightColor, ColorSliderLogsHighlight);
Control.AddOptionDropDown(CCVars.AdminPlayerTabSymbolSetting, DropDownPlayerTabSymbolSetting, playerTabSymbolSettings);
Control.AddOptionDropDown(CCVars.AdminPlayerTabRoleSetting, DropDownPlayerTabRoleSetting, playerTabRoleSettings);
Control.AddOptionDropDown(CCVars.AdminPlayerTabColorSetting, DropDownPlayerTabColorSetting, playerTabColorSettings);
@@ -104,12 +104,6 @@ namespace Content.Client.Options.UI.Tabs
_cfg.SaveToFile();
}
private void HandleToggleAutoGetUp(BaseButton.ButtonToggledEventArgs args) // WD EDIT
{
_cfg.SetCVar(GoobCVars.AutoGetUp, args.Pressed);
_cfg.SaveToFile();
}
public KeyRebindTab()
{
IoCManager.InjectDependencies(this);
@@ -168,9 +162,8 @@ namespace Content.Client.Options.UI.Tabs
AddButton(EngineKeyFunctions.MoveRight);
AddButton(EngineKeyFunctions.Walk);
AddCheckBox("ui-options-hotkey-toggle-walk", _cfg.GetCVar(CCVars.ToggleWalk), HandleToggleWalk);
AddButton(ContentKeyFunctions.ToggleStanding);
AddCheckBox("ui-options-function-auto-get-up", _cfg.GetCVar(GoobCVars.AutoGetUp), HandleToggleAutoGetUp); // WD EDIT
InitToggleWalk();
AddButton(ContentKeyFunctions.ToggleKnockdown);
AddHeader("ui-options-header-camera");
AddButton(EngineKeyFunctions.CameraRotateLeft);
@@ -94,7 +94,7 @@ public sealed class ClientsidePlaytimeTrackingManager
return;
}
// At less than 1 minute of time diff, there's not much point, and saving regardless will brick tests
// At less than 1 minute of time diff, there's not much point
// The reason this isn't checking for 0 is because TotalMinutes is fractional, rather than solely whole minutes
if (timeDiffMinutes < 1)
return;
@@ -5,4 +5,5 @@ namespace Content.Client.Power.Components;
[RegisterComponent]
public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent
{
public override float Load { get; set; }
}
@@ -11,6 +11,8 @@ namespace Content.Client.Radiation.Overlays;
public sealed class RadiationDebugOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IResourceCache _cache = default!;
private readonly SharedMapSystem _mapSystem;
private readonly RadiationSystem _radiation;
@@ -24,8 +26,7 @@ public sealed class RadiationDebugOverlay : Overlay
_radiation = _entityManager.System<RadiationSystem>();
_mapSystem = _entityManager.System<SharedMapSystem>();
var cache = IoCManager.Resolve<IResourceCache>();
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 8);
_font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 8);
}
protected override void Draw(in OverlayDrawArgs args)
@@ -28,6 +28,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public EntityUid Entity;
private bool _allowBorgControl = true;
public RoboticsConsoleWindow()
{
RobustXamlLoader.Load(this);
@@ -72,6 +74,7 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public void UpdateState(RoboticsConsoleState state)
{
_cyborgs = state.Cyborgs;
_allowBorgControl = state.AllowBorgControl;
// clear invalid selection
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
@@ -85,8 +88,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
PopulateData();
var locked = _lock.IsLocked(Entity);
DangerZone.Visible = !locked;
LockedMessage.Visible = locked;
DangerZone.Visible = !locked && _allowBorgControl;
LockedMessage.Visible = locked && _allowBorgControl; // Only show if locked AND control is allowed
}
private void PopulateCyborgs()
@@ -120,11 +123,19 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!);
var batteryColor = data.Charge switch {
< 0.2f => "red",
< 0.4f => "orange",
< 0.6f => "yellow",
< 0.8f => "green",
_ => "blue"
< 0.2f => "#FF6C7F", // red
< 0.4f => "#EF973C", // orange
< 0.6f => "#E8CB2D", // yellow
< 0.8f => "#30CC19", // green
_ => "#00D3B8" // cyan
};
var hpPercentColor = data.HpPercent switch {
< 0.2f => "#FF6C7F", // red
< 0.4f => "#EF973C", // orange
< 0.6f => "#E8CB2D", // yellow
< 0.8f => "#30CC19", // green
_ => "#00D3B8" // cyan
};
var text = new FormattedMessage();
@@ -132,12 +143,14 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
text.AddMarkupOrThrow(Loc.GetString("robotics-console-designation"));
text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color]
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-battery", ("charge", (int)(data.Charge * 100f)), ("color", batteryColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-hp", ("hp", (int)(data.HpPercent * 100f)), ("color", hpPercentColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-brain", ("brain", data.HasBrain))}\n");
text.AddMarkupOrThrow(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount)));
BorgInfo.SetMessage(text);
// how the turntables
DisableButton.Disabled = !(data.HasBrain && data.CanDisable);
DisableButton.Disabled = !_allowBorgControl || !(data.HasBrain && data.CanDisable);
DestroyButton.Disabled = !_allowBorgControl;
}
protected override void FrameUpdate(FrameEventArgs args)
@@ -24,7 +24,7 @@ public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem
return;
if (!_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component))
return;
state = RotationState.Vertical;
switch (state)
{
@@ -16,20 +16,20 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
get => _enableShuttlePosition;
set
{
if (_enableShuttlePosition == value) return;
if (_enableShuttlePosition == value)
return;
_enableShuttlePosition = value;
var overlayManager = IoCManager.Resolve<IOverlayManager>();
if (_enableShuttlePosition)
{
_overlay = new EmergencyShuttleOverlay(EntityManager.TransformQuery, XformSystem);
overlayManager.AddOverlay(_overlay);
_overlays.AddOverlay(_overlay);
RaiseNetworkEvent(new EmergencyShuttleRequestPositionMessage());
}
else
{
overlayManager.RemoveOverlay(_overlay!);
_overlays.RemoveOverlay(_overlay!);
_overlay = null;
}
}
@@ -40,6 +40,8 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
private readonly HashSet<DockingPortState> _drawnDocks = new();
private readonly Dictionary<DockingPortState, Button> _dockButtons = new();
private readonly Color _fallbackHighlightedColor = Color.Magenta;
/// <summary>
/// Store buttons for every other dock
/// </summary>
@@ -213,11 +215,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
if (HighlightedDock == dock.Entity)
{
otherDockColor = Color.ToSrgb(Color.Magenta);
otherDockColor = Color.ToSrgb(dock.HighlightedColor);
}
else
{
otherDockColor = Color.ToSrgb(Color.Purple);
otherDockColor = Color.ToSrgb(dock.Color);
}
/*
@@ -311,7 +313,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)),
ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation)));
var dockColor = Color.Magenta;
var dockColor = _viewedState?.HighlightedColor ?? _fallbackHighlightedColor;
var connectionColor = Color.Pink;
handle.DrawRect(ourDockConnection, connectionColor.WithAlpha(0.2f));
@@ -310,7 +310,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
-dockRadius * UIScale,
(Size.X + dockRadius) * UIScale,
(Size.Y + dockRadius) * UIScale);
if (_docks.TryGetValue(nent, out var docks))
{
foreach (var state in docks)
@@ -323,7 +323,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
continue;
}
var color = Color.ToSrgb(Color.Magenta);
var color = Color.ToSrgb(state.HighlightedColor);
var verts = new[]
{
@@ -12,6 +12,8 @@ public sealed class EmitterSystem : SharedEmitterSystem
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EmitterComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
+1 -16
View File
@@ -12,7 +12,6 @@ namespace Content.Client.Stack
{
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly ItemCounterSystem _counterSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -28,22 +27,8 @@ namespace Content.Client.Stack
base.SetCount(uid, amount, component);
if (component.Lingering &&
TryComp<SpriteComponent>(uid, out var sprite))
{
// tint the stack gray and make it transparent if it's lingering.
var color = component.Count == 0 && component.Lingering
? Color.DarkGray.WithAlpha(0.65f)
: Color.White;
for (var i = 0; i < sprite.AllLayers.Count(); i++)
{
_sprite.LayerSetColor((uid, sprite), i, color);
}
}
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0 && !component.Lingering)
if (component.Count <= 0)
{
Xform.DetachEntity(uid, Transform(uid));
return;
+2 -31
View File
@@ -2,34 +2,5 @@
namespace Content.Client.Station;
/// <summary>
/// This handles letting the client know stations are a thing. Only really used by an admin menu.
/// </summary>
public sealed partial class StationSystem : SharedStationSystem
{
private readonly List<(string Name, NetEntity Entity)> _stations = new();
/// <summary>
/// All stations that currently exist.
/// </summary>
/// <remarks>
/// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
/// </remarks>
// TODO: Stations have a global PVS override now, this can probably be changed into a query.
public IReadOnlyList<(string Name, NetEntity Entity)> Stations => _stations;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
}
private void StationsUpdated(StationsUpdatedEvent ev)
{
_stations.Clear();
// TODO this needs to be done in component states and with the Ensure() methods
_stations.AddRange(ev.Stations);
}
}
/// <inheritdoc/>
public sealed partial class StationSystem : SharedStationSystem;
@@ -1,10 +0,0 @@
using Content.Shared.Storage.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Storage.Components;
[RegisterComponent]
public sealed partial class EntityStorageComponent : SharedEntityStorageComponent
{
}
@@ -31,7 +31,7 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
SubscribeLocalEvent<EntityStorageComponent, ComponentHandleState>(OnHandleState);
}
public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref SharedEntityStorageComponent? component)
public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref EntityStorageComponent? component)
{
if (component != null)
return true;
@@ -40,6 +40,11 @@ public sealed class StorageSystem : SharedStorageSystem
component.MaxItemSize = state.MaxItemSize;
component.Whitelist = state.Whitelist;
component.Blacklist = state.Blacklist;
component.StorageInsertSound = state.StorageInsertSound;
component.StorageRemoveSound = state.StorageRemoveSound;
component.StorageOpenSound = state.StorageOpenSound;
component.StorageCloseSound = state.StorageCloseSound;
component.DefaultStorageOrientation = state.DefaultStorageOrientation;
_oldStoredItems.Clear();
+1 -1
View File
@@ -21,7 +21,7 @@
Name="RefundButton"
MinWidth="64"
HorizontalAlignment="Right"
Text="Refund" />
Text="{Loc 'store-ui-refund-text'}" />
</BoxContainer>
<LineEdit Name="SearchBar" Margin="4" PlaceHolder="Search" HorizontalExpand="True"/>
<PanelContainer VerticalExpand="True">
+22 -4
View File
@@ -1,17 +1,19 @@
using System.Numerics;
using Content.Shared.Mobs;
using System.Numerics;
using Content.Shared.CombatMode;
using Content.Shared.Interaction;
using Content.Shared.Stunnable;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Stunnable;
public sealed class StunSystem : SharedStunSystem
{
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
@@ -23,6 +25,22 @@ public sealed class StunSystem : SharedStunSystem
SubscribeLocalEvent<StunVisualsComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<StunVisualsComponent, AppearanceChangeEvent>(OnAppearanceChanged);
CommandBinds.Builder
.BindAfter(EngineKeyFunctions.UseSecondary, new PointerInputCmdHandler(OnUseSecondary, true, true), typeof(SharedInteractionSystem))
.Register<StunSystem>();
}
private bool OnUseSecondary(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (args.Session?.AttachedEntity is not {Valid: true} uid)
return false;
if (args.EntityUid != uid || !HasComp<KnockedDownComponent>(uid) || !_combat.IsInCombatMode(uid))
return false;
RaisePredictiveEvent(new ForceStandUpEvent());
return true;
}
/// <summary>

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