diff --git a/.editorconfig b/.editorconfig
index 186026ff1f5..3e71517eba6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -115,6 +115,8 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = false
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
+resharper_csharp_place_simple_embedded_statement_on_same_line = never
+resharper_csharp_keep_existing_embedded_arrangement = false
# Indentation preferences
#csharp_indent_block_contents = true
diff --git a/Content.Client/Administration/AdminNameOverlay.cs b/Content.Client/Administration/AdminNameOverlay.cs
index 01793a2fa1e..0d2526831de 100644
--- a/Content.Client/Administration/AdminNameOverlay.cs
+++ b/Content.Client/Administration/AdminNameOverlay.cs
@@ -1,3 +1,4 @@
+using Content.Client.Administration.Systems;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
diff --git a/Content.Client/Administration/Components/HeadstandComponent.cs b/Content.Client/Administration/Components/HeadstandComponent.cs
new file mode 100644
index 00000000000..403d3260929
--- /dev/null
+++ b/Content.Client/Administration/Components/HeadstandComponent.cs
@@ -0,0 +1,10 @@
+using Content.Shared.Administration.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Client.Administration.Components;
+
+[RegisterComponent, NetworkedComponent]
+public sealed class HeadstandComponent : SharedHeadstandComponent
+{
+
+}
diff --git a/Content.Client/Administration/QuickDialogSystem.cs b/Content.Client/Administration/QuickDialogSystem.cs
new file mode 100644
index 00000000000..fcd439d9869
--- /dev/null
+++ b/Content.Client/Administration/QuickDialogSystem.cs
@@ -0,0 +1,154 @@
+using System.Linq;
+using Content.Client.UserInterface;
+using Content.Shared.Administration;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.Administration;
+
+///
+/// This handles the client portion of quick dialogs.
+///
+public sealed class QuickDialogSystem : EntitySystem
+{
+ ///
+ public override void Initialize()
+ {
+ SubscribeNetworkEvent(OpenDialog);
+ }
+
+ private void OpenDialog(QuickDialogOpenEvent ev)
+ {
+ var window = new FancyWindow()
+ {
+ Title = ev.Title
+ };
+
+ var entryContainer = new BoxContainer()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Vertical,
+ Margin = new Thickness(8),
+ };
+
+ var promptsDict = new Dictionary();
+
+ foreach (var entry in ev.Prompts)
+ {
+ var entryBox = new BoxContainer()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Horizontal
+ };
+
+ entryBox.AddChild(new Label { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
+ var edit = new LineEdit() { HorizontalExpand = true};
+ entryBox.AddChild(edit);
+ switch (entry.Type)
+ {
+ case QuickDialogEntryType.Integer:
+ edit.IsValid += VerifyInt;
+ edit.PlaceHolder = "Integer..";
+ break;
+ case QuickDialogEntryType.Float:
+ edit.IsValid += VerifyFloat;
+ edit.PlaceHolder = "Float..";
+ break;
+ case QuickDialogEntryType.ShortText:
+ edit.IsValid += VerifyShortText;
+ edit.PlaceHolder = "Short text..";
+ break;
+ case QuickDialogEntryType.LongText:
+ edit.IsValid += VerifyLongText;
+ edit.PlaceHolder = "Long text..";
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ promptsDict.Add(entry.FieldId, edit);
+ entryContainer.AddChild(entryBox);
+ }
+
+ var buttonsBox = new BoxContainer()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Horizontal,
+ HorizontalAlignment = Control.HAlignment.Center,
+ };
+
+ var alreadyReplied = false;
+
+ if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
+ {
+ var okButton = new Button()
+ {
+ Text = "Ok",
+ };
+
+ okButton.OnPressed += _ =>
+ {
+ RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
+ promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
+ QuickDialogButtonFlag.OkButton));
+ alreadyReplied = true;
+ window.Close();
+ };
+
+ buttonsBox.AddChild(okButton);
+ }
+
+ if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
+ {
+ var cancelButton = new Button()
+ {
+ Text = "Cancel",
+ };
+
+ cancelButton.OnPressed += _ =>
+ {
+ RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
+ new(),
+ QuickDialogButtonFlag.CancelButton));
+ alreadyReplied = true;
+ window.Close();
+ };
+
+ buttonsBox.AddChild(cancelButton);
+ }
+
+ window.OnClose += () =>
+ {
+ if (!alreadyReplied)
+ {
+ RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
+ new(),
+ QuickDialogButtonFlag.CancelButton));
+ }
+ };
+
+ entryContainer.AddChild(buttonsBox);
+
+ window.ContentsContainer.AddChild(entryContainer);
+
+ window.MinWidth *= 2; // Just double it.
+
+ window.OpenCentered();
+ }
+
+ private bool VerifyInt(string input)
+ {
+ return int.TryParse(input, out var _);
+ }
+
+ private bool VerifyFloat(string input)
+ {
+ return float.TryParse(input, out var _);
+ }
+
+ private bool VerifyShortText(string input)
+ {
+ return input.Length <= 100;
+ }
+
+ private bool VerifyLongText(string input)
+ {
+ return input.Length <= 2000;
+ }
+}
diff --git a/Content.Client/Administration/AdminSystem.Menu.cs b/Content.Client/Administration/Systems/AdminSystem.Menu.cs
similarity index 85%
rename from Content.Client/Administration/AdminSystem.Menu.cs
rename to Content.Client/Administration/Systems/AdminSystem.Menu.cs
index 0def38a0990..c10cadd48a5 100644
--- a/Content.Client/Administration/AdminSystem.Menu.cs
+++ b/Content.Client/Administration/Systems/AdminSystem.Menu.cs
@@ -1,6 +1,6 @@
-using System.Collections.Generic;
using Content.Client.Administration.Managers;
using Content.Client.Administration.UI;
+using Content.Client.Administration.UI.Tabs.ObjectsTab;
using Content.Client.Administration.UI.Tabs.PlayerTab;
using Content.Client.HUD;
using Content.Client.Verbs;
@@ -11,13 +11,11 @@
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
-using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
-using Robust.Shared.IoC;
using Robust.Shared.Network;
-namespace Content.Client.Administration
+namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem
{
@@ -101,13 +99,18 @@ public void Open()
}
_window.PlayerTabControl.OnEntryPressed += PlayerTabEntryPressed;
+ _window.ObjectsTabControl.OnEntryPressed += ObjectsTabEntryPressed;
_window.OpenCentered();
}
public void Close()
{
if (_window != null)
+ {
_window.PlayerTabControl.OnEntryPressed -= PlayerTabEntryPressed;
+ _window.ObjectsTabControl.OnEntryPressed -= ObjectsTabEntryPressed;
+ }
+
_window?.Close();
foreach (var window in _commandWindows)
@@ -163,5 +166,23 @@ private void PlayerTabEntryPressed(BaseButton.ButtonEventArgs args)
args.Event.Handle();
}
+
+ private void ObjectsTabEntryPressed(BaseButton.ButtonEventArgs args)
+ {
+ if (args.Button is not ObjectsTabEntry button)
+ return;
+
+ var uid = button.AssocEntity;
+ var function = args.Event.Function;
+
+ if (function == EngineKeyFunctions.UIClick)
+ _clientConsoleHost.ExecuteCommand($"vv {uid}");
+ else if (function == ContentKeyFunctions.OpenContextMenu)
+ _verbSystem.VerbMenu.OpenVerbMenu(uid, true);
+ else
+ return;
+
+ args.Event.Handle();
+ }
}
}
diff --git a/Content.Client/Administration/AdminSystem.Overlay.cs b/Content.Client/Administration/Systems/AdminSystem.Overlay.cs
similarity index 96%
rename from Content.Client/Administration/AdminSystem.Overlay.cs
rename to Content.Client/Administration/Systems/AdminSystem.Overlay.cs
index febb9d6421b..85611626d50 100644
--- a/Content.Client/Administration/AdminSystem.Overlay.cs
+++ b/Content.Client/Administration/Systems/AdminSystem.Overlay.cs
@@ -1,7 +1,7 @@
using Content.Client.Administration.Managers;
using Robust.Client.Graphics;
-namespace Content.Client.Administration
+namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem
{
diff --git a/Content.Client/Administration/AdminSystem.cs b/Content.Client/Administration/Systems/AdminSystem.cs
similarity index 94%
rename from Content.Client/Administration/AdminSystem.cs
rename to Content.Client/Administration/Systems/AdminSystem.cs
index 137a4ea3fb7..de62caa4ef4 100644
--- a/Content.Client/Administration/AdminSystem.cs
+++ b/Content.Client/Administration/Systems/AdminSystem.cs
@@ -1,13 +1,10 @@
-using System;
-using System.Collections.Generic;
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Administration.Events;
using Content.Shared.GameTicking;
-using Robust.Shared.GameObjects;
using Robust.Shared.Network;
-namespace Content.Client.Administration
+namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem : EntitySystem
{
diff --git a/Content.Client/Administration/AdminVerbSystem.cs b/Content.Client/Administration/Systems/AdminVerbSystem.cs
similarity index 91%
rename from Content.Client/Administration/AdminVerbSystem.cs
rename to Content.Client/Administration/Systems/AdminVerbSystem.cs
index cb440a92dac..9a6bfd705d5 100644
--- a/Content.Client/Administration/AdminVerbSystem.cs
+++ b/Content.Client/Administration/Systems/AdminVerbSystem.cs
@@ -1,10 +1,7 @@
using Content.Shared.Verbs;
using Robust.Client.Console;
-using Robust.Client.ViewVariables;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-namespace Content.Client.Verbs
+namespace Content.Client.Administration.Systems
{
///
/// Client-side admin verb system. These usually open some sort of UIs.
diff --git a/Content.Client/Administration/BwoinkSystem.cs b/Content.Client/Administration/Systems/BwoinkSystem.cs
similarity index 96%
rename from Content.Client/Administration/BwoinkSystem.cs
rename to Content.Client/Administration/Systems/BwoinkSystem.cs
index c04b5fdfd32..96076d96cf2 100644
--- a/Content.Client/Administration/BwoinkSystem.cs
+++ b/Content.Client/Administration/Systems/BwoinkSystem.cs
@@ -1,7 +1,5 @@
#nullable enable
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using Content.Client.Administration.Managers;
using Content.Client.Administration.UI;
using Content.Client.Administration.UI.CustomControls;
@@ -9,16 +7,13 @@
using Content.Shared.Administration;
using JetBrains.Annotations;
using Robust.Client.Graphics;
-using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Player;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Player;
-using Robust.Shared.Localization;
+using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Audio;
-using Robust.Shared.IoC;
using Robust.Shared.Network;
+using Robust.Shared.Player;
-namespace Content.Client.Administration
+namespace Content.Client.Administration.Systems
{
[UsedImplicitly]
public sealed class BwoinkSystem : SharedBwoinkSystem
diff --git a/Content.Client/Administration/Systems/HeadstandSystem.cs b/Content.Client/Administration/Systems/HeadstandSystem.cs
new file mode 100644
index 00000000000..d0634e4ddd6
--- /dev/null
+++ b/Content.Client/Administration/Systems/HeadstandSystem.cs
@@ -0,0 +1,35 @@
+using Content.Client.Administration.Components;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Administration.Systems;
+
+public sealed class HeadstandSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnHeadstandAdded);
+ SubscribeLocalEvent(OnHeadstandRemoved);
+ }
+
+ private void OnHeadstandAdded(EntityUid uid, HeadstandComponent component, ComponentStartup args)
+ {
+ if (!TryComp(uid, out var sprite))
+ return;
+
+ foreach (var layer in sprite.AllLayers)
+ {
+ layer.Rotation += Angle.FromDegrees(180.0f);
+ }
+ }
+
+ private void OnHeadstandRemoved(EntityUid uid, HeadstandComponent component, ComponentShutdown args)
+ {
+ if (!TryComp(uid, out var sprite))
+ return;
+
+ foreach (var layer in sprite.AllLayers)
+ {
+ layer.Rotation -= Angle.FromDegrees(180.0f);
+ }
+ }
+}
diff --git a/Content.Client/Administration/KillSignSystem.cs b/Content.Client/Administration/Systems/KillSignSystem.cs
similarity index 96%
rename from Content.Client/Administration/KillSignSystem.cs
rename to Content.Client/Administration/Systems/KillSignSystem.cs
index 2959593bb96..5d86c0d91ef 100644
--- a/Content.Client/Administration/KillSignSystem.cs
+++ b/Content.Client/Administration/Systems/KillSignSystem.cs
@@ -2,7 +2,7 @@
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
-namespace Content.Client.Administration;
+namespace Content.Client.Administration.Systems;
public sealed class KillSignSystem : EntitySystem
{
diff --git a/Content.Client/Administration/UI/AdminAnnounceWindow.xaml b/Content.Client/Administration/UI/AdminAnnounceWindow.xaml
index bb5c29bc788..a703a7f5e62 100644
--- a/Content.Client/Administration/UI/AdminAnnounceWindow.xaml
+++ b/Content.Client/Administration/UI/AdminAnnounceWindow.xaml
@@ -1,15 +1,15 @@
-
+
-
+
-
+
diff --git a/Content.Client/Administration/UI/AdminAnnounceWindow.xaml.cs b/Content.Client/Administration/UI/AdminAnnounceWindow.xaml.cs
index 0cc0a8f61b2..653a9c2a98b 100644
--- a/Content.Client/Administration/UI/AdminAnnounceWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminAnnounceWindow.xaml.cs
@@ -19,9 +19,9 @@ public AdminAnnounceWindow()
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- AnnounceMethod.AddItem(_localization.GetString("announce-type-station"));
+ AnnounceMethod.AddItem(_localization.GetString("admin-announce-type-station"));
AnnounceMethod.SetItemMetadata(0, AdminAnnounceType.Station);
- AnnounceMethod.AddItem(_localization.GetString("announce-type-server"));
+ AnnounceMethod.AddItem(_localization.GetString("admin-announce-type-server"));
AnnounceMethod.SetItemMetadata(1, AdminAnnounceType.Server);
AnnounceMethod.OnItemSelected += AnnounceMethodOnOnItemSelected;
Announcement.OnTextChanged += AnnouncementOnOnTextChanged;
diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml b/Content.Client/Administration/UI/AdminMenuWindow.xaml
index f148f5c1be9..458184db357 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml
@@ -4,7 +4,8 @@
xmlns:adminbusTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminbusTab"
xmlns:atmosTab="clr-namespace:Content.Client.Administration.UI.Tabs.AtmosTab"
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
- xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab">
+ xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
+ xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab">
@@ -12,5 +13,6 @@
+
diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
index be0c5da45d3..561e958f602 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
@@ -25,6 +25,7 @@ public AdminMenuWindow()
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-players-tab"));
+ MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-objects-tab"));
}
protected override void EnteredTree()
diff --git a/Content.Client/Administration/UI/BwoinkWindow.xaml.cs b/Content.Client/Administration/UI/BwoinkWindow.xaml.cs
index 73067155128..a514cb95211 100644
--- a/Content.Client/Administration/UI/BwoinkWindow.xaml.cs
+++ b/Content.Client/Administration/UI/BwoinkWindow.xaml.cs
@@ -3,6 +3,7 @@
using System.Text;
using System.Threading;
using Content.Client.Administration.Managers;
+using Content.Client.Administration.Systems;
using Content.Client.Administration.UI.Tabs.AdminTab;
using Content.Client.Stylesheets;
using Content.Shared.Administration;
diff --git a/Content.Client/Administration/UI/CustomControls/BwoinkPanel.xaml.cs b/Content.Client/Administration/UI/CustomControls/BwoinkPanel.xaml.cs
index 582147b69b7..70714b9247d 100644
--- a/Content.Client/Administration/UI/CustomControls/BwoinkPanel.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/BwoinkPanel.xaml.cs
@@ -1,5 +1,6 @@
#nullable enable
using System;
+using Content.Client.Administration.Systems;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
index 48f58a6efab..f2579348e05 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Content.Client.Administration.Systems;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
diff --git a/Content.Client/Administration/UI/PermissionsEui.cs b/Content.Client/Administration/UI/PermissionsEui.cs
index f79a767ca5c..202df1b7b3d 100644
--- a/Content.Client/Administration/UI/PermissionsEui.cs
+++ b/Content.Client/Administration/UI/PermissionsEui.cs
@@ -201,7 +201,7 @@ public override void HandleState(EuiStateBase state)
_ranks = s.AdminRanks;
_menu.AdminsList.RemoveAllChildren();
- foreach (var admin in s.Admins)
+ foreach (var admin in s.Admins.OrderBy(d => d.UserName))
{
var al = _menu.AdminsList;
var name = admin.UserName ?? admin.UserId.ToString();
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
index 21acf472045..2d643c9f7c5 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
@@ -8,14 +8,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
new file mode 100644
index 00000000000..2a13604e99f
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
new file mode 100644
index 00000000000..b581a217154
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
@@ -0,0 +1,73 @@
+using System.Linq;
+using Content.Client.Station;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
+
+[GenerateTypedNameReferences]
+public sealed partial class ObjectsTab : Control
+{
+ [Dependency] private readonly EntityManager _entityManager = default!;
+
+ private readonly List _objects = new();
+ private List _selections = new();
+
+ public event Action? OnEntryPressed;
+
+ public ObjectsTab()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ ObjectTypeOptions.OnItemSelected += ev =>
+ {
+ ObjectTypeOptions.SelectId(ev.Id);
+ RefreshObjectList(_selections[ev.Id]);
+ };
+
+ foreach (var type in Enum.GetValues(typeof(ObjectsTabSelection)))
+ {
+ _selections.Add((ObjectsTabSelection)type!);
+ ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type!)!);
+ }
+
+ RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
+ }
+
+ private void RefreshObjectList(ObjectsTabSelection selection)
+ {
+ var entities = selection switch
+ {
+ ObjectsTabSelection.Stations => _entityManager.EntitySysManager.GetEntitySystem().Stations.ToList(),
+ ObjectsTabSelection.Grids => _entityManager.EntityQuery(true).Select(x => x.Owner).ToList(),
+ ObjectsTabSelection.Maps => _entityManager.EntityQuery(true).Select(x => x.Owner).ToList(),
+ _ => throw new ArgumentOutOfRangeException(nameof(selection), selection, null)
+ };
+
+ foreach (var control in _objects)
+ {
+ ObjectList.RemoveChild(control);
+ }
+
+ _objects.Clear();
+
+ foreach (var entity in entities)
+ {
+ var ctrl = new ObjectsTabEntry(_entityManager.GetComponent(entity).EntityName, entity);
+ _objects.Add(ctrl);
+ ObjectList.AddChild(ctrl);
+ ctrl.OnPressed += args => OnEntryPressed?.Invoke(args);
+ }
+ }
+
+ private enum ObjectsTabSelection
+ {
+ Grids,
+ Maps,
+ Stations,
+ }
+}
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
new file mode 100644
index 00000000000..92d5278ab5c
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
new file mode 100644
index 00000000000..98cfe53af19
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
@@ -0,0 +1,19 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
+
+[GenerateTypedNameReferences]
+public sealed partial class ObjectsTabEntry : ContainerButton
+{
+ public EntityUid AssocEntity;
+
+ public ObjectsTabEntry(string name, EntityUid euid)
+ {
+ RobustXamlLoader.Load(this);
+ AssocEntity = euid;
+ EIDLabel.Text = euid.ToString();
+ NameLabel.Text = name;
+ }
+}
diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
index 84a4817da64..c7bd84f7675 100644
--- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
@@ -1,3 +1,4 @@
+using Content.Client.Administration.Systems;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
diff --git a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs
index b45836a437b..dc37dca7a33 100644
--- a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs
+++ b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs
@@ -1,264 +1,70 @@
using Content.Client.Atmos.Overlays;
-using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.GameTicking;
using JetBrains.Annotations;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
-using Robust.Client.Utility;
using Robust.Shared.Utility;
namespace Content.Client.Atmos.EntitySystems
{
[UsedImplicitly]
- internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
+ public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
- [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
+ [Dependency] private readonly IOverlayManager _overlayMan = default!;
+ [Dependency] private readonly SpriteSystem _spriteSys = default!;
- // Gas overlays
- public readonly float[] Timer = new float[Atmospherics.TotalNumberOfGases];
- public readonly float[][] FrameDelays = new float[Atmospherics.TotalNumberOfGases][];
- public readonly int[] FrameCounter = new int[Atmospherics.TotalNumberOfGases];
- public readonly Texture[][] Frames = new Texture[Atmospherics.TotalNumberOfGases][];
-
- // Fire overlays
- public const int FireStates = 3;
- public const string FireRsiPath = "/Textures/Effects/fire.rsi";
-
- public readonly float[] FireTimer = new float[FireStates];
- public readonly float[][] FireFrameDelays = new float[FireStates][];
- public readonly int[] FireFrameCounter = new int[FireStates];
- public readonly Texture[][] FireFrames = new Texture[FireStates][];
-
- private readonly Dictionary> _tileData =
- new();
-
- public const int GasOverlayZIndex = 1;
+ private GasTileOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeNetworkEvent(HandleGasOverlayMessage);
+ SubscribeNetworkEvent(HandleGasOverlayUpdate);
SubscribeLocalEvent(OnGridRemoved);
- for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
- {
- var overlay = _atmosphereSystem.GetOverlay(i);
- switch (overlay)
- {
- case SpriteSpecifier.Rsi animated:
- var rsi = _resourceCache.GetResource(animated.RsiPath).RSI;
- var stateId = animated.RsiState;
-
- if(!rsi.TryGetState(stateId, out var state)) continue;
-
- Frames[i] = state.GetFrames(RSI.State.Direction.South);
- FrameDelays[i] = state.GetDelays();
- FrameCounter[i] = 0;
- break;
- case SpriteSpecifier.Texture texture:
- Frames[i] = new[] {texture.Frame0()};
- FrameDelays[i] = Array.Empty();
- break;
- case null:
- Frames[i] = Array.Empty();
- FrameDelays[i] = Array.Empty();
- break;
- }
- }
-
- var fire = _resourceCache.GetResource(FireRsiPath).RSI;
-
- for (var i = 0; i < FireStates; i++)
- {
- if (!fire.TryGetState((i+1).ToString(), out var state))
- throw new ArgumentOutOfRangeException($"Fire RSI doesn't have state \"{i}\"!");
-
- FireFrames[i] = state.GetFrames(RSI.State.Direction.South);
- FireFrameDelays[i] = state.GetDelays();
- FireFrameCounter[i] = 0;
- }
-
- var overlayManager = IoCManager.Resolve();
- overlayManager.AddOverlay(new GasTileOverlay());
- overlayManager.AddOverlay(new FireTileOverlay());
+ _overlay = new GasTileOverlay(this, _resourceCache, ProtoMan, _spriteSys);
+ _overlayMan.AddOverlay(_overlay);
}
- private void HandleGasOverlayMessage(GasOverlayMessage message)
+ public override void Reset(RoundRestartCleanupEvent ev)
{
- foreach (var (indices, data) in message.OverlayData)
- {
- var chunk = GetOrCreateChunk(message.GridId, indices);
- chunk.Update(data, indices);
- }
- }
-
- // Slightly different to the server-side system version
- private GasOverlayChunk GetOrCreateChunk(EntityUid gridId, Vector2i indices)
- {
- if (!_tileData.TryGetValue(gridId, out var chunks))
- {
- chunks = new Dictionary();
- _tileData[gridId] = chunks;
- }
-
- var chunkIndices = GetGasChunkIndices(indices);
-
- if (!chunks.TryGetValue(chunkIndices, out var chunk))
- {
- chunk = new GasOverlayChunk(gridId, chunkIndices);
- chunks[chunkIndices] = chunk;
- }
-
- return chunk;
+ _overlay.TileData.Clear();
}
public override void Shutdown()
{
base.Shutdown();
- var overlayManager = IoCManager.Resolve();
- overlayManager.RemoveOverlay();
- overlayManager.RemoveOverlay();
- }
-
- private void OnGridRemoved(GridRemovalEvent ev)
- {
- _tileData.Remove(ev.EntityUid);
- }
-
- public bool HasData(EntityUid gridId)
- {
- return _tileData.ContainsKey(gridId);
- }
-
- public GasOverlayEnumerator GetOverlays(EntityUid gridIndex, Vector2i indices)
- {
- if (!_tileData.TryGetValue(gridIndex, out var chunks))
- return default;
-
- var chunkIndex = GetGasChunkIndices(indices);
- if (!chunks.TryGetValue(chunkIndex, out var chunk))
- return default;
-
- var overlays = chunk.GetData(indices);
-
- return new GasOverlayEnumerator(overlays, this);
- }
-
- public FireOverlayEnumerator GetFireOverlays(EntityUid gridIndex, Vector2i indices)
- {
- if (!_tileData.TryGetValue(gridIndex, out var chunks))
- return default;
-
- var chunkIndex = GetGasChunkIndices(indices);
- if (!chunks.TryGetValue(chunkIndex, out var chunk))
- return default;
-
- var overlays = chunk.GetData(indices);
-
- return new FireOverlayEnumerator(overlays, this);
+ _overlayMan.RemoveOverlay(_overlay);
}
- public override void FrameUpdate(float frameTime)
+ private void HandleGasOverlayUpdate(GasOverlayUpdateEvent ev)
{
- base.FrameUpdate(frameTime);
-
- for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
+ foreach (var (grid, removedIndicies) in ev.RemovedChunks)
{
- var delays = FrameDelays[i];
- if (delays.Length == 0) continue;
-
- var frameCount = FrameCounter[i];
- Timer[i] += frameTime;
- var time = delays[frameCount];
-
- if (Timer[i] < time)
+ if (!_overlay.TileData.TryGetValue(grid, out var chunks))
continue;
- Timer[i] -= time;
- FrameCounter[i] = (frameCount + 1) % Frames[i].Length;
- }
-
- for (var i = 0; i < FireStates; i++)
- {
- var delays = FireFrameDelays[i];
- if (delays.Length == 0) continue;
-
- var frameCount = FireFrameCounter[i];
- FireTimer[i] += frameTime;
- var time = delays[frameCount];
-
- if (FireTimer[i] < time) continue;
- FireTimer[i] -= time;
- FireFrameCounter[i] = (frameCount + 1) % FireFrames[i].Length;
- }
- }
-
- public struct GasOverlayEnumerator
- {
- private readonly GasTileOverlaySystem _system;
- private readonly GasData[]? _data;
- // TODO: Take Fire Temperature into account, when we code fire color
-
- private readonly int _length; // We cache the length so we can avoid a pointer dereference, for speed. Brrr.
- private int _current;
-
- public GasOverlayEnumerator(in GasOverlayData data, GasTileOverlaySystem system)
- {
- // Gas can't be null, as the caller to this constructor already ensured it wasn't.
- _data = data.Gas;
-
- _system = system;
-
- _length = _data?.Length ?? 0;
- _current = 0;
+ foreach (var index in removedIndicies)
+ {
+ chunks.Remove(index);
+ }
}
- public bool MoveNext(out (Texture Texture, Color Color) overlay)
+ foreach (var (grid, gridData) in ev.UpdatedChunks)
{
- if (_current < _length)
+ var chunks = _overlay.TileData.GetOrNew(grid);
+ foreach (var chunkData in gridData)
{
- // Data can't be null here unless length/current are incorrect
- var gas = _data![_current++];
- var frames = _system.Frames[gas.Index];
- overlay = (frames[_system.FrameCounter[gas.Index]], Color.White.WithAlpha(gas.Opacity));
- return true;
+ chunks[chunkData.Index] = chunkData;
}
-
- overlay = default;
- return false;
}
}
- public struct FireOverlayEnumerator
+ private void OnGridRemoved(GridRemovalEvent ev)
{
- private readonly GasTileOverlaySystem _system;
- private byte _fireState;
- // TODO: Take Fire Temperature into account, when we code fire color
-
- public FireOverlayEnumerator(in GasOverlayData data, GasTileOverlaySystem system)
- {
- _fireState = data.FireState;
- _system = system;
- }
- public bool MoveNext(out (Texture Texture, Color Color) overlay)
- {
-
- if (_fireState != 0)
- {
- var state = _fireState - 1;
- var frames = _system.FireFrames[state];
- // TODO ATMOS Set color depending on temperature
- overlay = (frames[_system.FireFrameCounter[state]], Color.White);
-
- // Setting this to zero so we don't get stuck in an infinite loop.
- _fireState = 0;
- return true;
- }
-
- overlay = default;
- return false;
- }
+ _overlay.TileData.Remove(ev.EntityUid);
}
}
}
diff --git a/Content.Client/Atmos/Overlays/FireTileOverlay.cs b/Content.Client/Atmos/Overlays/FireTileOverlay.cs
deleted file mode 100644
index b32f8699fc6..00000000000
--- a/Content.Client/Atmos/Overlays/FireTileOverlay.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Content.Client.Atmos.EntitySystems;
-using Robust.Client.Graphics;
-using Robust.Shared.Enums;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Map;
-
-namespace Content.Client.Atmos.Overlays
-{
- public sealed class FireTileOverlay : Overlay
- {
- private readonly GasTileOverlaySystem _gasTileOverlaySystem;
-
- [Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-
- public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
- private readonly ShaderInstance _shader;
-
- public FireTileOverlay()
- {
- IoCManager.InjectDependencies(this);
-
- _gasTileOverlaySystem = EntitySystem.Get();
- _shader = _prototypeManager.Index("unshaded").Instance().Duplicate();
- ZIndex = GasTileOverlaySystem.GasOverlayZIndex + 1;
- }
-
- protected override void Draw(in OverlayDrawArgs args)
- {
- var drawHandle = args.WorldHandle;
-
- var mapId = args.Viewport.Eye!.Position.MapId;
- var worldBounds = args.WorldBounds;
-
- drawHandle.UseShader(_shader);
-
- foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
- {
- if (!_gasTileOverlaySystem.HasData(mapGrid.GridEntityId))
- continue;
-
- drawHandle.SetTransform(mapGrid.WorldMatrix);
-
- foreach (var tile in mapGrid.GetTilesIntersecting(worldBounds))
- {
- var enumerator = _gasTileOverlaySystem.GetFireOverlays(mapGrid.GridEntityId, tile.GridIndices);
- while (enumerator.MoveNext(out var tuple))
- {
- drawHandle.DrawTexture(tuple.Texture, new Vector2(tile.X, tile.Y), tuple.Color);
- }
- }
- }
-
- drawHandle.SetTransform(Matrix3.Identity);
- }
- }
-}
diff --git a/Content.Client/Atmos/Overlays/GasTileOverlay.cs b/Content.Client/Atmos/Overlays/GasTileOverlay.cs
index b97eaa15dc4..ed98936e919 100644
--- a/Content.Client/Atmos/Overlays/GasTileOverlay.cs
+++ b/Content.Client/Atmos/Overlays/GasTileOverlay.cs
@@ -1,54 +1,208 @@
using Content.Client.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.Prototypes;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
using Robust.Shared.Map;
-using Robust.Shared.Maths;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Client.Atmos.Overlays
{
public sealed class GasTileOverlay : Overlay
{
- private readonly GasTileOverlaySystem _gasTileOverlaySystem;
+ private readonly GasTileOverlaySystem _system;
+ private readonly IMapManager _mapManager;
+
+ public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;
+ private readonly ShaderInstance _shader;
- [Dependency] private readonly IMapManager _mapManager = default!;
+ public readonly Dictionary> TileData = new();
- public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
+ // Gas overlays
+ private readonly float[] _timer;
+ private readonly float[][] _frameDelays;
+ private readonly int[] _frameCounter;
- public GasTileOverlay()
+ // TODO combine textures into a single texture atlas.
+ private readonly Texture[][] _frames;
+
+ // Fire overlays
+ private const int FireStates = 3;
+ private const string FireRsiPath = "/Textures/Effects/fire.rsi";
+
+ private readonly float[] _fireTimer = new float[FireStates];
+ private readonly float[][] _fireFrameDelays = new float[FireStates][];
+ private readonly int[] _fireFrameCounter = new int[FireStates];
+ private readonly Texture[][] _fireFrames = new Texture[FireStates][];
+
+ private int _gasCount;
+
+ public const int GasOverlayZIndex = (int) Content.Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
+
+ public GasTileOverlay(GasTileOverlaySystem system, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys)
+ {
+ _system = system;
+ _mapManager = IoCManager.Resolve();
+ _shader = protoMan.Index("unshaded").Instance();
+ ZIndex = GasOverlayZIndex;
+
+ _gasCount = _system.VisibleGasId.Length;
+ _timer = new float[_gasCount];
+ _frameDelays = new float[_gasCount][];
+ _frameCounter = new int[_gasCount];
+ _frames = new Texture[_gasCount][];
+
+ for (var i = 0; i < _gasCount; i++)
+ {
+ var gasPrototype = protoMan.Index(_system.VisibleGasId[i].ToString());
+
+ SpriteSpecifier overlay;
+
+ if (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayState))
+ overlay = new SpriteSpecifier.Rsi(new ResourcePath(gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState);
+ else if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture))
+ overlay = new SpriteSpecifier.Texture(new ResourcePath(gasPrototype.GasOverlayTexture));
+ else
+ continue;
+
+ switch (overlay)
+ {
+ case SpriteSpecifier.Rsi animated:
+ var rsi = resourceCache.GetResource(animated.RsiPath).RSI;
+ var stateId = animated.RsiState;
+
+ if (!rsi.TryGetState(stateId, out var state)) continue;
+
+ _frames[i] = state.GetFrames(RSI.State.Direction.South);
+ _frameDelays[i] = state.GetDelays();
+ _frameCounter[i] = 0;
+ break;
+ case SpriteSpecifier.Texture texture:
+ _frames[i] = new[] { spriteSys.Frame0(texture) };
+ _frameDelays[i] = Array.Empty();
+ break;
+ }
+ }
+
+ var fire = resourceCache.GetResource(FireRsiPath).RSI;
+
+ for (var i = 0; i < FireStates; i++)
+ {
+ if (!fire.TryGetState((i + 1).ToString(), out var state))
+ throw new ArgumentOutOfRangeException($"Fire RSI doesn't have state \"{i}\"!");
+
+ _fireFrames[i] = state.GetFrames(RSI.State.Direction.South);
+ _fireFrameDelays[i] = state.GetDelays();
+ _fireFrameCounter[i] = 0;
+ }
+ }
+ protected override void FrameUpdate(FrameEventArgs args)
{
- IoCManager.InjectDependencies(this);
+ base.FrameUpdate(args);
+
+ for (var i = 0; i < _gasCount; i++)
+ {
+ var delays = _frameDelays[i];
+ if (delays.Length == 0) continue;
- _gasTileOverlaySystem = EntitySystem.Get();
- ZIndex = GasTileOverlaySystem.GasOverlayZIndex;
+ var frameCount = _frameCounter[i];
+ _timer[i] += args.DeltaSeconds;
+ var time = delays[frameCount];
+
+ if (_timer[i] < time)
+ continue;
+
+ _timer[i] -= time;
+ _frameCounter[i] = (frameCount + 1) % _frames[i].Length;
+ }
+
+ for (var i = 0; i < FireStates; i++)
+ {
+ var delays = _fireFrameDelays[i];
+ if (delays.Length == 0) continue;
+
+ var frameCount = _fireFrameCounter[i];
+ _fireTimer[i] += args.DeltaSeconds;
+ var time = delays[frameCount];
+
+ if (_fireTimer[i] < time) continue;
+ _fireTimer[i] -= time;
+ _fireFrameCounter[i] = (frameCount + 1) % _fireFrames[i].Length;
+ }
}
protected override void Draw(in OverlayDrawArgs args)
{
var drawHandle = args.WorldHandle;
- var mapId = args.Viewport.Eye!.Position.MapId;
- var worldBounds = args.WorldBounds;
-
- drawHandle.UseShader(null);
-
- foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
+ foreach (var mapGrid in _mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds))
{
- if (!_gasTileOverlaySystem.HasData(mapGrid.GridEntityId))
+ if (!TileData.TryGetValue(mapGrid.GridEntityId, out var gridData))
continue;
drawHandle.SetTransform(mapGrid.WorldMatrix);
+ var floatBounds = mapGrid.InvWorldMatrix.TransformBox(in args.WorldBounds);
+ var localBounds = new Box2i(
+ (int) MathF.Floor(floatBounds.Left),
+ (int) MathF.Floor(floatBounds.Bottom),
+ (int) MathF.Ceiling(floatBounds.Right),
+ (int) MathF.Ceiling(floatBounds.Top));
+
+ // Currently it would be faster to group drawing by gas rather than by chunk, but if the textures are
+ // ever moved to a single atlas, that should no longer be the case. So this is just grouping draw calls
+ // by chunk, even though its currently slower.
- foreach (var tile in mapGrid.GetTilesIntersecting(worldBounds))
+ drawHandle.UseShader(null);
+ foreach (var chunk in gridData.Values)
{
- var enumerator = _gasTileOverlaySystem.GetOverlays(mapGrid.GridEntityId, tile.GridIndices);
- while (enumerator.MoveNext(out var tuple))
+ var enumerator = new GasChunkEnumerator(chunk);
+
+ while (enumerator.MoveNext(out var gas))
{
- drawHandle.DrawTexture(tuple.Texture, new Vector2(tile.X, tile.Y), tuple.Color);
+ if (gas.Value.Opacity == null)
+ continue;
+
+ var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y);
+ if (!localBounds.Contains(tilePosition))
+ continue;
+
+ for (var i = 0; i < _gasCount; i++)
+ {
+ var opacity = gas.Value.Opacity[i];
+ if (opacity > 0)
+ drawHandle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
+ }
+ }
+ }
+
+ // And again for fire, with the unshaded shader
+ drawHandle.UseShader(_shader);
+ foreach (var chunk in gridData.Values)
+ {
+ var enumerator = new GasChunkEnumerator(chunk);
+
+ while (enumerator.MoveNext(out var gas))
+ {
+ if (gas.Value.FireState == 0)
+ continue;
+
+ var index = chunk.Origin + (enumerator.X, enumerator.Y);
+ if (!localBounds.Contains(index))
+ continue;
+
+ var state = gas.Value.FireState - 1;
+ var texture = _fireFrames[state][_fireFrameCounter[state]];
+ drawHandle.DrawTexture(texture, index);
}
}
}
+
+ drawHandle.UseShader(null);
+ drawHandle.SetTransform(Matrix3.Identity);
}
}
}
diff --git a/Content.Client/Buckle/BuckleSystem.cs b/Content.Client/Buckle/BuckleSystem.cs
index e558ab70af5..90f13f38546 100644
--- a/Content.Client/Buckle/BuckleSystem.cs
+++ b/Content.Client/Buckle/BuckleSystem.cs
@@ -1,9 +1,26 @@
+using Content.Client.Buckle.Strap;
using Content.Shared.Buckle;
+using Content.Shared.Buckle.Components;
+using Robust.Shared.GameStates;
namespace Content.Client.Buckle
{
internal sealed class BuckleSystem : SharedBuckleSystem
{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnStrapHandleState);
+ }
+ private void OnStrapHandleState(EntityUid uid, StrapComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not StrapComponentState state) return;
+ component.Position = state.Position;
+ component.BuckleOffsetUnclamped = state.BuckleOffsetClamped;
+ component.BuckledEntities.Clear();
+ component.BuckledEntities.UnionWith(state.BuckledEntities);
+ component.MaxBuckleDistance = state.MaxBuckleDistance;
+ }
}
}
diff --git a/Content.Client/Buckle/Strap/StrapComponent.cs b/Content.Client/Buckle/Strap/StrapComponent.cs
index 27da0cfe3e9..efdc42fe8fe 100644
--- a/Content.Client/Buckle/Strap/StrapComponent.cs
+++ b/Content.Client/Buckle/Strap/StrapComponent.cs
@@ -1,6 +1,5 @@
using Content.Shared.Buckle.Components;
using Content.Shared.DragDrop;
-using Robust.Shared.GameObjects;
namespace Content.Client.Buckle.Strap
{
diff --git a/Content.Client/Commands/OpenAHelpCommand.cs b/Content.Client/Commands/OpenAHelpCommand.cs
index 6cff7c08eb0..5d6ecfaed1a 100644
--- a/Content.Client/Commands/OpenAHelpCommand.cs
+++ b/Content.Client/Commands/OpenAHelpCommand.cs
@@ -1,5 +1,6 @@
using System;
using Content.Client.Administration;
+using Content.Client.Administration.Systems;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
diff --git a/Content.Client/Communications/UI/CommunicationsConsoleMenu.cs b/Content.Client/Communications/UI/CommunicationsConsoleMenu.cs
index c0781f2fd88..293ab3ba5e4 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleMenu.cs
+++ b/Content.Client/Communications/UI/CommunicationsConsoleMenu.cs
@@ -35,7 +35,7 @@ public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
SizeFlagsStretchRatio = 1
};
AnnounceButton = new Button();
- AnnounceButton.Text = "Announce";
+ AnnounceButton.Text = Loc.GetString("comms-console-menu-announcement-button");
AnnounceButton.OnPressed += (_) => Owner.AnnounceButtonPressed(_messageInput.Text.Trim());
AnnounceButton.Disabled = !owner.CanAnnounce;
diff --git a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs
index 977f9bbe3ba..d6fb41aafd2 100644
--- a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs
+++ b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using Content.Client.Stylesheets;
using Content.Shared.Decals;
using Robust.Client.AutoGenerated;
@@ -179,7 +179,8 @@ public void Populate()
_decals = new Dictionary();
foreach (var decalPrototype in prototypes)
{
- _decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
+ if (decalPrototype.ShowMenu)
+ _decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
}
RefreshList();
diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index 305fab29945..50d991f8110 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -183,10 +183,10 @@ public override void PostInit()
ContentContexts.SetupContexts(inputMan.Contexts);
IoCManager.Resolve().Initialize();
- IoCManager.Resolve().LoadParallax(); // Have to do this later because prototypes are needed.
+ IoCManager.Resolve().LoadDefaultParallax(); // Have to do this later because prototypes are needed.
var overlayMgr = IoCManager.Resolve();
- overlayMgr.AddOverlay(new ParallaxOverlay());
+
overlayMgr.AddOverlay(new SingularityOverlay());
overlayMgr.AddOverlay(new FlashOverlay());
overlayMgr.AddOverlay(new RadiationPulseOverlay());
diff --git a/Content.Client/EscapeMenu/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/EscapeMenu/UI/Tabs/KeyRebindTab.xaml.cs
index 56b038a7b0c..d1045b6ccec 100644
--- a/Content.Client/EscapeMenu/UI/Tabs/KeyRebindTab.xaml.cs
+++ b/Content.Client/EscapeMenu/UI/Tabs/KeyRebindTab.xaml.cs
@@ -172,6 +172,15 @@ void AddCheckBox(string checkBoxName, bool currentState, Action(OnPlayerAttached);
SubscribeLocalEvent(OnPlayerDetached);
+ SubscribeNetworkEvent(RoundRestartCleanup);
+
_overlay = new();
}
@@ -52,4 +62,9 @@ private void OnBlindShutdown(EntityUid uid, BlindableComponent component, Compon
_overlayMan.RemoveOverlay(_overlay);
}
}
+
+ private void RoundRestartCleanup(RoundRestartCleanupEvent ev)
+ {
+ _lightManager.Enabled = true;
+ }
}
diff --git a/Content.Client/Eye/EyeLerpingSystem.cs b/Content.Client/Eye/EyeLerpingSystem.cs
index f687005f465..7befae2fb6f 100644
--- a/Content.Client/Eye/EyeLerpingSystem.cs
+++ b/Content.Client/Eye/EyeLerpingSystem.cs
@@ -94,7 +94,7 @@ private void LerpPlayerEye(float frameTime)
return;
// We can't lerp if the mob can't move!
- if (!TryComp(mob, out IMoverComponent? mover))
+ if (!TryComp(mob, out InputMoverComponent? mover))
return;
LerpEye(_eyeManager.CurrentEye, frameTime, mover.LastGridAngle, _playerActiveEye);
diff --git a/Content.Client/Gravity/GravitySystem.cs b/Content.Client/Gravity/GravitySystem.cs
index 2bcf53d146e..3efaa6ff968 100644
--- a/Content.Client/Gravity/GravitySystem.cs
+++ b/Content.Client/Gravity/GravitySystem.cs
@@ -2,7 +2,7 @@
namespace Content.Client.Gravity
{
- internal sealed class GravitySystem : SharedGravitySystem
+ public sealed class GravitySystem : SharedGravitySystem
{
}
diff --git a/Content.Client/HUD/GameHud.cs b/Content.Client/HUD/GameHud.cs
index e8c91826d79..5b22c04aa0d 100644
--- a/Content.Client/HUD/GameHud.cs
+++ b/Content.Client/HUD/GameHud.cs
@@ -3,6 +3,7 @@
using Content.Client.HUD.UI;
using Content.Client.Info;
using Content.Client.Administration;
+using Content.Client.Administration.Systems;
using Content.Client.Resources;
using Content.Client.Targeting;
using Content.Shared.CCVar;
diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs
index 945811af5e6..fe46cf4c263 100644
--- a/Content.Client/Input/ContentContexts.cs
+++ b/Content.Client/Input/ContentContexts.cs
@@ -33,6 +33,11 @@ public static void SetupContexts(IInputContextContainer contexts)
common.AddFunction(ContentKeyFunctions.EditorCopyObject);
var human = contexts.GetContext("human");
+ human.AddFunction(EngineKeyFunctions.MoveUp);
+ human.AddFunction(EngineKeyFunctions.MoveDown);
+ human.AddFunction(EngineKeyFunctions.MoveLeft);
+ human.AddFunction(EngineKeyFunctions.MoveRight);
+ human.AddFunction(EngineKeyFunctions.Walk);
human.AddFunction(ContentKeyFunctions.SwapHands);
human.AddFunction(ContentKeyFunctions.Drop);
human.AddFunction(ContentKeyFunctions.UseItemInHand);
@@ -48,7 +53,6 @@ public static void SetupContexts(IInputContextContainer contexts)
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
- human.AddFunction(ContentKeyFunctions.Fart);
human.AddFunction(ContentKeyFunctions.MouseMiddle);
human.AddFunction(ContentKeyFunctions.ArcadeUp);
human.AddFunction(ContentKeyFunctions.ArcadeDown);
@@ -90,7 +94,7 @@ public static void SetupContexts(IInputContextContainer contexts)
aghost.AddFunction(ContentKeyFunctions.Drop);
aghost.AddFunction(ContentKeyFunctions.ThrowItemInHand);
- var ghost = contexts.New("ghost", "common");
+ var ghost = contexts.New("ghost", "human");
ghost.AddFunction(EngineKeyFunctions.MoveUp);
ghost.AddFunction(EngineKeyFunctions.MoveDown);
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
diff --git a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
index 7ab5e5f5fdb..669d05622e6 100644
--- a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
+++ b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
@@ -142,14 +142,18 @@ private bool PlayCheck()
if (instrumentEnt == null || instrument == null)
return false;
- // If we're a handheld instrument, we might be in a container. Get it just in case.
- instrumentEnt.Value.TryGetContainerMan(out var conMan);
-
var localPlayer = IoCManager.Resolve().LocalPlayer;
// If we don't have a player or controlled entity, we return.
if (localPlayer?.ControlledEntity == null) return false;
+ // By default, allow an instrument to play itself and skip all other checks
+ if (localPlayer.ControlledEntity == instrumentEnt)
+ return true;
+
+ // If we're a handheld instrument, we might be in a container. Get it just in case.
+ instrumentEnt.Value.TryGetContainerMan(out var conMan);
+
// If the instrument is handheld and we're not holding it, we return.
if ((instrument.Handheld && (conMan == null
|| conMan.Owner != localPlayer.ControlledEntity))) return false;
diff --git a/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml b/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml
index 125f360cbbf..d01a01cadff 100644
--- a/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml
+++ b/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml
@@ -1,4 +1,4 @@
-
+
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/Content.Client/MobState/MobStateSystem.cs b/Content.Client/MobState/MobStateSystem.cs
index 743f31fcaf1..4753a0fd9b4 100644
--- a/Content.Client/MobState/MobStateSystem.cs
+++ b/Content.Client/MobState/MobStateSystem.cs
@@ -55,6 +55,7 @@ private void OnPlayerAttach(PlayerAttachedEvent ev)
{
if (TryComp(ev.Entity, out var mobState) && TryComp(ev.Entity, out var damageable))
{
+ _overlay.DeadLevel = 0f;
SetLevel(mobState, damageable.TotalDamage);
}
else
diff --git a/Content.Client/MobState/Overlays/DamageOverlay.cs b/Content.Client/MobState/Overlays/DamageOverlay.cs
index 80ceaede735..743af6f21b5 100644
--- a/Content.Client/MobState/Overlays/DamageOverlay.cs
+++ b/Content.Client/MobState/Overlays/DamageOverlay.cs
@@ -40,7 +40,7 @@ public sealed class DamageOverlay : Overlay
private float _oldCritLevel = 0f;
- private float _deadLevel = 1f;
+ public float DeadLevel = 1f;
public DamageOverlay()
{
@@ -64,7 +64,6 @@ protected override void Draw(in OverlayDrawArgs args)
var viewport = args.ViewportBounds;
var handle = args.ScreenHandle;
var distance = args.ViewportBounds.Width;
- var lerpRate = 0.2f;
var time = (float) _timing.RealTime.TotalSeconds;
var lastFrameTime = (float) _timing.FrameTime.TotalSeconds;
@@ -72,30 +71,30 @@ protected override void Draw(in OverlayDrawArgs args)
// If they just died then lerp out the white overlay.
if (State != DamageState.Dead)
{
- _deadLevel = 1f;
+ DeadLevel = 1f;
}
- else if (!_deadLevel.Equals(0f))
+ else if (!DeadLevel.Equals(0f))
{
- var diff = -_deadLevel;
- _deadLevel += GetDiff(diff, lerpRate, lastFrameTime);
+ var diff = -DeadLevel;
+ DeadLevel += GetDiff(diff, lastFrameTime);
}
- if (!_oldBruteLevel.Equals(BruteLevel))
+ if (!MathHelper.CloseTo(_oldBruteLevel, BruteLevel, 0.001f))
{
var diff = BruteLevel - _oldBruteLevel;
- _oldBruteLevel += GetDiff(diff, lerpRate, lastFrameTime);
+ _oldBruteLevel += GetDiff(diff, lastFrameTime);
}
- if (!_oldOxygenLevel.Equals(OxygenLevel))
+ if (!MathHelper.CloseTo(_oldOxygenLevel, OxygenLevel, 0.001f))
{
var diff = OxygenLevel - _oldOxygenLevel;
- _oldOxygenLevel += GetDiff(diff, lerpRate, lastFrameTime);
+ _oldOxygenLevel += GetDiff(diff, lastFrameTime);
}
- if (!_oldCritLevel.Equals(CritLevel))
+ if (!MathHelper.CloseTo(_oldCritLevel, CritLevel, 0.001f))
{
var diff = CritLevel - _oldCritLevel;
- _oldCritLevel += GetDiff(diff, lerpRate, lastFrameTime);
+ _oldCritLevel += GetDiff(diff, lastFrameTime);
}
/*
@@ -190,7 +189,7 @@ protected override void Draw(in OverlayDrawArgs args)
handle.DrawRect(viewport, Color.White);
}
- level = State != DamageState.Dead ? _oldCritLevel : _deadLevel;
+ level = State != DamageState.Dead ? _oldCritLevel : DeadLevel;
if (level > 0f)
{
@@ -217,8 +216,15 @@ protected override void Draw(in OverlayDrawArgs args)
}
}
- private float GetDiff(float value, float lerpRate, float lastFrameTime)
+ private float GetDiff(float value, float lastFrameTime)
{
- return Math.Clamp(value, -1 * lerpRate * lastFrameTime, lerpRate * lastFrameTime);
+ var adjustment = value * 5f * lastFrameTime;
+
+ if (value < 0f)
+ adjustment = Math.Clamp(adjustment, value, -value);
+ else
+ adjustment = Math.Clamp(adjustment, -value, value);
+
+ return adjustment;
}
}
diff --git a/Content.Client/Nuke/NukeMenu.xaml.cs b/Content.Client/Nuke/NukeMenu.xaml.cs
index 644513a4a73..a71d410e8b1 100644
--- a/Content.Client/Nuke/NukeMenu.xaml.cs
+++ b/Content.Client/Nuke/NukeMenu.xaml.cs
@@ -106,7 +106,7 @@ public void UpdateState(NukeUiState state)
EjectButton.Disabled = !state.DiskInserted || state.Status == NukeStatus.ARMED;
AnchorButton.Disabled = state.Status == NukeStatus.ARMED;
AnchorButton.Pressed = state.IsAnchored;
- ArmButton.Disabled = !state.AllowArm;
+ ArmButton.Disabled = !state.AllowArm || !state.IsAnchored;
}
private string VisualizeCode(int codeLength, int maxLength)
diff --git a/Content.Client/PDA/PDABoundUserInterface.cs b/Content.Client/PDA/PDABoundUserInterface.cs
index 48edfecf2c4..818e8247fc9 100644
--- a/Content.Client/PDA/PDABoundUserInterface.cs
+++ b/Content.Client/PDA/PDABoundUserInterface.cs
@@ -79,14 +79,16 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (msg.PDAOwnerInfo.IdOwner != null || msg.PDAOwnerInfo.JobTitle != null)
{
_menu.IdInfoLabel.SetMarkup(Loc.GetString("comp-pda-ui",
- ("Owner",msg.PDAOwnerInfo.IdOwner ?? "Unknown"),
- ("JobTitle",msg.PDAOwnerInfo.JobTitle ?? "Unassigned")));
+ ("Owner",msg.PDAOwnerInfo.IdOwner ?? Loc.GetString("comp-pda-ui-unknown")),
+ ("JobTitle",msg.PDAOwnerInfo.JobTitle ?? Loc.GetString("comp-pda-ui-unassigned"))));
}
else
{
_menu.IdInfoLabel.SetMarkup(Loc.GetString("comp-pda-ui-blank"));
}
+ _menu.StationNameLabel.SetMarkup(Loc.GetString("comp-pda-ui-station", ("Station",msg.StationName ?? Loc.GetString("comp-pda-ui-unknown"))));
+
_menu.EjectIdButton.Visible = msg.PDAOwnerInfo.IdOwner != null || msg.PDAOwnerInfo.JobTitle != null;
_menu.EjectPenButton.Visible = msg.HasPen;
_menu.ActivateUplinkButton.Visible = msg.HasUplink;
diff --git a/Content.Client/PDA/PDAMenu.xaml b/Content.Client/PDA/PDAMenu.xaml
index 40c31b9b2dc..e17c2416c8a 100644
--- a/Content.Client/PDA/PDAMenu.xaml
+++ b/Content.Client/PDA/PDAMenu.xaml
@@ -8,9 +8,12 @@
HorizontalExpand="True"
MinSize="50 50">
+
-
public sealed class ParallaxControl : Control
{
+ [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IParallaxManager _parallaxManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
- [ViewVariables(VVAccess.ReadWrite)] public Vector2i Offset { get; set; }
+ [ViewVariables(VVAccess.ReadWrite)] public Vector2 Offset { get; set; }
public ParallaxControl()
{
@@ -24,20 +24,24 @@ public ParallaxControl()
Offset = (_random.Next(0, 1000), _random.Next(0, 1000));
RectClipContent = true;
+ _parallaxManager.LoadParallaxByName("FastSpace");
}
protected override void Draw(DrawingHandleScreen handle)
{
- foreach (var layer in _parallaxManager.ParallaxLayers)
+ foreach (var layer in _parallaxManager.GetParallaxLayers("FastSpace"))
{
var tex = layer.Texture;
- var texSize = tex.Size * layer.Config.Scale.Floored();
+ var texSize = (tex.Size.X * (int) Size.X, tex.Size.Y * (int) Size.X) * layer.Config.Scale.Floored() / 540;
var ourSize = PixelSize;
+ var currentTime = (float) _timing.RealTime.TotalSeconds;
+ var offset = Offset + new Vector2(currentTime * 100f, currentTime * 0f);
+
if (layer.Config.Tiled)
{
// Multiply offset by slowness to match normal parallax
- var scaledOffset = (Offset * layer.Config.Slowness).Floored();
+ var scaledOffset = (offset * layer.Config.Slowness).Floored();
// Then modulo the scaled offset by the size to prevent drawing a bunch of offscreen tiles for really small images.
scaledOffset.X %= texSize.X;
diff --git a/Content.Client/Parallax/ParallaxOverlay.cs b/Content.Client/Parallax/ParallaxOverlay.cs
index 89b27194263..1f401cde913 100644
--- a/Content.Client/Parallax/ParallaxOverlay.cs
+++ b/Content.Client/Parallax/ParallaxOverlay.cs
@@ -1,47 +1,55 @@
-using System;
using Content.Client.Parallax.Managers;
using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
-using Robust.Shared.IoC;
-using Robust.Shared.Maths;
+using Robust.Shared.Map;
using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
namespace Content.Client.Parallax;
public sealed class ParallaxOverlay : Overlay
{
- [Dependency] private readonly IParallaxManager _parallaxManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly IParallaxManager _manager = default!;
+ private readonly ParallaxSystem _parallax;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
- private readonly ShaderInstance _shader;
public ParallaxOverlay()
{
IoCManager.InjectDependencies(this);
- _shader = _prototypeManager.Index("unshaded").Instance();
+ _parallax = IoCManager.Resolve().GetEntitySystem();
+
}
protected override void Draw(in OverlayDrawArgs args)
{
- if (args.Viewport.Eye == null)
- {
+ if (args.MapId == MapId.Nullspace)
return;
- }
if (!_configurationManager.GetCVar(CCVars.ParallaxEnabled))
- {
return;
- }
+ var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
var screenHandle = args.WorldHandle;
- screenHandle.UseShader(_shader);
- foreach (var layer in _parallaxManager.ParallaxLayers)
+ var layers = _parallax.GetParallaxLayers(args.MapId);
+ var realTime = (float) _timing.RealTime.TotalSeconds;
+
+ foreach (var layer in layers)
{
+ ShaderInstance? shader;
+
+ if (!string.IsNullOrEmpty(layer.Config.Shader))
+ shader = _prototypeManager.Index(layer.Config.Shader).Instance();
+ else
+ shader = null;
+
+ screenHandle.UseShader(shader);
var tex = layer.Texture;
// Size of the texture in world units.
@@ -52,10 +60,11 @@ protected override void Draw(in OverlayDrawArgs args)
// The effects of this are such that a slowness of 1 anchors the layer to the centre of the screen, while a slowness of 0 anchors the layer to the world.
// (For values 0.0 to 1.0 this is in effect a lerp, but it's deliberately unclamped.)
// The ParallaxAnchor adapts the parallax for station positioning and possibly map-specific tweaks.
- var home = layer.Config.WorldHomePosition + _parallaxManager.ParallaxAnchor;
+ var home = layer.Config.WorldHomePosition + _manager.ParallaxAnchor;
+ var scrolled = layer.Config.Scrolling * realTime;
// Origin - start with the parallax shift itself.
- var originBL = (args.Viewport.Eye.Position.Position - home) * layer.Config.Slowness;
+ var originBL = (position - home) * layer.Config.Slowness + scrolled;
// Place at the home.
originBL += home;
@@ -90,6 +99,8 @@ protected override void Draw(in OverlayDrawArgs args)
screenHandle.DrawTextureRect(tex, Box2.FromDimensions(originBL, size));
}
}
+
+ screenHandle.UseShader(null);
}
}
diff --git a/Content.Client/Parallax/ParallaxSystem.cs b/Content.Client/Parallax/ParallaxSystem.cs
new file mode 100644
index 00000000000..d3320ce1f97
--- /dev/null
+++ b/Content.Client/Parallax/ParallaxSystem.cs
@@ -0,0 +1,71 @@
+using Content.Client.Parallax.Managers;
+using Content.Shared.Parallax;
+using Robust.Client.Graphics;
+using Robust.Shared.GameStates;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Parallax;
+
+public sealed class ParallaxSystem : SharedParallaxSystem
+{
+ [Dependency] private readonly IMapManager _map = default!;
+ [Dependency] private readonly IOverlayManager _overlay = default!;
+ [Dependency] private readonly IParallaxManager _parallax = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+
+ private const string Fallback = "Default";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _overlay.AddOverlay(new ParallaxOverlay());
+ SubscribeLocalEvent(OnParallaxHandleState);
+ _protoManager.PrototypesReloaded += OnReload;
+ }
+
+ private void OnReload(PrototypesReloadedEventArgs obj)
+ {
+ _parallax.UnloadParallax(Fallback);
+ _parallax.LoadDefaultParallax();
+
+ foreach (var comp in EntityQuery(true))
+ {
+ _parallax.UnloadParallax(comp.Parallax);
+ _parallax.LoadParallaxByName(comp.Parallax);
+ }
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _overlay.RemoveOverlay();
+ _protoManager.PrototypesReloaded -= OnReload;
+ }
+
+ private void OnParallaxHandleState(EntityUid uid, ParallaxComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not ParallaxComponentState state) return;
+ component.Parallax = state.Parallax;
+
+ if (!_parallax.IsLoaded(component.Parallax))
+ {
+ _parallax.LoadParallaxByName(component.Parallax);
+ }
+ }
+
+ public ParallaxLayerPrepared[] GetParallaxLayers(MapId mapId)
+ {
+ return _parallax.GetParallaxLayers(GetParallax(_map.GetMapEntityId(mapId)));
+ }
+
+ public string GetParallax(MapId mapId)
+ {
+ return GetParallax(_map.GetMapEntityId(mapId));
+ }
+
+ public string GetParallax(EntityUid mapUid)
+ {
+ return TryComp(mapUid, out var parallax) ? parallax.Parallax : Fallback;
+ }
+}
diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs
index 6db507f00ee..51292f767bb 100644
--- a/Content.Client/Physics/Controllers/MoverController.cs
+++ b/Content.Client/Physics/Controllers/MoverController.cs
@@ -1,10 +1,7 @@
-using Content.Shared.MobState.Components;
-using Content.Shared.Movement;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Pulling.Components;
using Robust.Client.Player;
-using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Player;
using Robust.Shared.Timing;
@@ -20,14 +17,47 @@ public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
- if (_playerManager.LocalPlayer?.ControlledEntity is not {Valid: true} player ||
- !TryComp(player, out IMoverComponent? mover) ||
- !TryComp(player, out PhysicsComponent? body) ||
+ if (_playerManager.LocalPlayer?.ControlledEntity is not {Valid: true} player)
+ return;
+
+ if (TryComp(player, out var relayMover))
+ {
+ if (relayMover.RelayEntity != null)
+ HandleClientsideMovement(relayMover.RelayEntity.Value, frameTime);
+ }
+
+ HandleClientsideMovement(player, frameTime);
+ }
+
+ private void HandleClientsideMovement(EntityUid player, float frameTime)
+ {
+ if (!TryComp(player, out InputMoverComponent? mover) ||
!TryComp(player, out TransformComponent? xform))
{
return;
}
+ PhysicsComponent? body = null;
+ TransformComponent? xformMover = xform;
+
+ if (mover.ToParent && HasComp(xform.ParentUid))
+ {
+ if (!TryComp(xform.ParentUid, out body) ||
+ !TryComp(xform.ParentUid, out xformMover))
+ {
+ return;
+ }
+
+ if (TryComp(xform.ParentUid, out var parentMover))
+ {
+ mover.LastGridAngle = parentMover.LastGridAngle;
+ }
+ }
+ else if (!TryComp(player, out body))
+ {
+ return;
+ }
+
if (xform.GridUid != null)
mover.LastGridAngle = GetParentGridAngle(xform, mover);
@@ -65,13 +95,7 @@ public override void UpdateBeforeSolve(bool prediction, float frameTime)
}
// Server-side should just be handled on its own so we'll just do this shizznit
- if (TryComp(player, out IMobMoverComponent? mobMover))
- {
- HandleMobMovement(mover, body, mobMover, xform, frameTime);
- return;
- }
-
- HandleKinematicMovement(mover, body);
+ HandleMobMovement(mover, body, xformMover, frameTime);
}
protected override Filter GetSoundPlayers(EntityUid mover)
diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs
index aea17fec808..3841164bcbf 100644
--- a/Content.Client/Popups/PopupSystem.cs
+++ b/Content.Client/Popups/PopupSystem.cs
@@ -157,7 +157,8 @@ private static string GetStyleClass(PopupType type) =>
public override void FrameUpdate(float frameTime)
{
- if (_aliveWorldLabels.Count == 0) return;
+ if (_aliveWorldLabels.Count == 0 && _aliveCursorLabels.Count == 0)
+ return;
var player = _playerManager.LocalPlayer?.ControlledEntity;
var playerPos = player != null ? Transform(player.Value).MapPosition : MapCoordinates.Nullspace;
@@ -234,13 +235,13 @@ private sealed class CursorPopupLabel : PopupLabel
public CursorPopupLabel(ScreenCoordinates screenCoords) : base()
{
- InitialPos = screenCoords.Position / UIScale - DesiredSize / 2;
+ InitialPos = screenCoords.Position - DesiredSize / 2;
}
protected override void FrameUpdate(FrameEventArgs eventArgs)
{
base.FrameUpdate(eventArgs);
- LayoutContainer.SetPosition(this, InitialPos - (0, 20 * (TotalTime * TotalTime + TotalTime)));
+ LayoutContainer.SetPosition(this, InitialPos / UIScale - (0, 20 * (TotalTime * TotalTime + TotalTime)));
}
}
diff --git a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
index 755e277b7f9..ceaa965e59f 100644
--- a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
+++ b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
@@ -2,8 +2,6 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
namespace Content.Client.Research.UI
{
@@ -11,8 +9,8 @@ namespace Content.Client.Research.UI
public sealed partial class ResearchClientServerSelectionMenu : DefaultWindow
{
private int _serverCount;
- private string[] _serverNames = System.Array.Empty();
- private int[] _serverIds = System.Array.Empty();
+ private string[] _serverNames = Array.Empty();
+ private int[] _serverIds = Array.Empty();
private int _selectedServerId = -1;
private ResearchClientBoundUserInterface Owner { get; }
diff --git a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
index 63f71f8dc2f..d4056584201 100644
--- a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
+++ b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
@@ -18,7 +18,6 @@ protected override void Open()
{
base.Open();
_window = new ShuttleConsoleWindow();
- _window.ShuttleModePressed += OnShuttleModePressed;
_window.UndockPressed += OnUndockPressed;
_window.StartAutodockPressed += OnAutodockPressed;
_window.StopAutodockPressed += OnStopAutodockPressed;
@@ -65,11 +64,6 @@ private void OnUndockPressed(EntityUid obj)
SendMessage(new UndockRequestMessage() {DockEntity = obj});
}
- private void OnShuttleModePressed(ShuttleMode obj)
- {
- SendMessage(new ShuttleModeRequestMessage() {Mode = obj});
- }
-
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
diff --git a/Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs
index 7ae419c358b..f9e01c09fd8 100644
--- a/Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs
+++ b/Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs
@@ -1,16 +1,43 @@
+using Content.Shared.Input;
using Content.Shared.Shuttles.Components;
-using Content.Shared.Shuttles.Events;
using Content.Shared.Shuttles.Systems;
+using Robust.Client.Input;
+using Robust.Client.Player;
using Robust.Shared.GameStates;
namespace Content.Client.Shuttles.Systems
{
public sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
{
+ [Dependency] private readonly IInputManager _input = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnHandleState);
+ var shuttle = _input.Contexts.New("shuttle", "common");
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleStrafeUp);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleStrafeDown);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleStrafeLeft);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleStrafeRight);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleRotateLeft);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleRotateRight);
+ shuttle.AddFunction(ContentKeyFunctions.ShuttleBrake);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _input.Contexts.Remove("shuttle");
+ }
+
+ protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
+ {
+ base.HandlePilotShutdown(uid, component, args);
+ if (_playerManager.LocalPlayer?.ControlledEntity != uid) return;
+
+ _input.Contexts.SetActiveContext("human");
}
private void OnHandleState(EntityUid uid, PilotComponent component, ref ComponentHandleState args)
@@ -21,6 +48,7 @@ private void OnHandleState(EntityUid uid, PilotComponent component, ref Componen
if (!console.IsValid())
{
component.Console = null;
+ _input.Contexts.SetActiveContext("human");
return;
}
@@ -32,6 +60,7 @@ private void OnHandleState(EntityUid uid, PilotComponent component, ref Componen
component.Console = shuttleConsoleComponent;
ActionBlockerSystem.UpdateCanMove(uid);
+ _input.Contexts.SetActiveContext("shuttle");
}
}
}
diff --git a/Content.Client/Shuttles/UI/DockingControl.cs b/Content.Client/Shuttles/UI/DockingControl.cs
index 2c37f623570..7daa9c18986 100644
--- a/Content.Client/Shuttles/UI/DockingControl.cs
+++ b/Content.Client/Shuttles/UI/DockingControl.cs
@@ -16,7 +16,6 @@ public class DockingControl : Control
private readonly IEntityManager _entManager;
private readonly IMapManager _mapManager;
- private const int MinimapRadius = 384;
private const int MinimapMargin = 4;
private float _range = 8f;
@@ -24,8 +23,8 @@ public class DockingControl : Control
private const float GridLinesDistance = 32f;
private int MidPoint => SizeFull / 2;
- private int SizeFull => (int) ((MinimapRadius + MinimapMargin) * 2 * UIScale);
- private int ScaledMinimapRadius => (int) (MinimapRadius * UIScale);
+ private int SizeFull => (int) ((RadarControl.MinimapRadius + MinimapMargin) * 2 * UIScale);
+ private int ScaledMinimapRadius => (int) (RadarControl.MinimapRadius * UIScale);
private float MinimapScale => _range != 0 ? ScaledMinimapRadius / _range : 0f;
public EntityUid? ViewedDock;
diff --git a/Content.Client/Shuttles/UI/RadarControl.cs b/Content.Client/Shuttles/UI/RadarControl.cs
index fd846746c03..01bd88fc25e 100644
--- a/Content.Client/Shuttles/UI/RadarControl.cs
+++ b/Content.Client/Shuttles/UI/RadarControl.cs
@@ -22,7 +22,7 @@ public sealed class RadarControl : Control
private const float ScrollSensitivity = 8f;
- private const int MinimapRadius = 320;
+ public const int MinimapRadius = 320;
private const int MinimapMargin = 4;
private const float GridLinesDistance = 32f;
@@ -73,6 +73,7 @@ public RadarControl()
{
IoCManager.InjectDependencies(this);
MinSize = (SizeFull, SizeFull);
+ RectClipContent = true;
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
diff --git a/Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml b/Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml
index 548c136d0aa..fe80948aa86 100644
--- a/Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml
+++ b/Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml
@@ -83,10 +83,6 @@
Align="Right"/>
-