diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index da9d4d693a8129..2b6d556117ef65 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -55,3 +55,10 @@
#Jezi
/Content.*/Medical @Jezithyr
/Content.*/Body @Jezithyr
+
+# Sloth
+/Content.*/Audio @metalgearsloth
+/Content.*/Movement @metalgearsloth
+/Content.*/NPC @metalgearsloth
+/Content.*/Shuttles @metalgearsloth
+/Content.*/Weapons @metalgearsloth
diff --git a/.github/workflows/labeler-untriaged.yml b/.github/workflows/labeler-untriaged.yml
index 630122aa0878b7..775aab265468af 100644
--- a/.github/workflows/labeler-untriaged.yml
+++ b/.github/workflows/labeler-untriaged.yml
@@ -9,5 +9,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions-ecosystem/action-add-labels@v1
+ if: join(github.event.issue.labels) == ''
with:
labels: "Status: Untriaged"
diff --git a/.github/workflows/rsi-diff.yml b/.github/workflows/rsi-diff.yml
index 1f122526d738a2..98cc97e9221d7c 100644
--- a/.github/workflows/rsi-diff.yml
+++ b/.github/workflows/rsi-diff.yml
@@ -15,9 +15,12 @@ jobs:
- name: Get changed files
id: files
- uses: Ana06/get-changed-files@v1.2
+ uses: Ana06/get-changed-files@v2.3.0
with:
format: 'space-delimited'
+ filter: |
+ **.rsi
+ **.png
- name: Diff changed RSIs
id: diff
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 0e0d3ae890cd79..00000000000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "omnisharp.analyzeOpenDocumentsOnly": true,
- "dotnet.defaultSolution": "SpaceStation14.sln"
-}
diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
index f3aa2572f2f99b..d5c43e2a500e70 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
@@ -3,38 +3,57 @@
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
-namespace Content.Client.Administration.UI
+namespace Content.Client.Administration.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminMenuWindow : DefaultWindow
{
- [GenerateTypedNameReferences]
- public sealed partial class AdminMenuWindow : DefaultWindow
+ public event Action? OnDisposed;
+
+ public AdminMenuWindow()
+ {
+ MinSize = new Vector2(650, 250);
+ Title = Loc.GetString("admin-menu-title");
+ RobustXamlLoader.Load(this);
+ MasterTabContainer.SetTabTitle((int) TabIndex.Admin, Loc.GetString("admin-menu-admin-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Adminbus, Loc.GetString("admin-menu-adminbus-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Atmos, Loc.GetString("admin-menu-atmos-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Round, Loc.GetString("admin-menu-round-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-tab"));
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+ MasterTabContainer.SetTabTitle((int) TabIndex.BabyJail, Loc.GetString("admin-menu-baby-jail-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Players, Loc.GetString("admin-menu-players-tab"));
+ MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab"));
+ MasterTabContainer.OnTabChanged += OnTabChanged;
+ }
+
+ private void OnTabChanged(int tabIndex)
{
- public event Action? OnDisposed;
+ var tabEnum = (TabIndex)tabIndex;
+ if (tabEnum == TabIndex.Objects)
+ ObjectsTabControl.RefreshObjectList();
+ }
- public AdminMenuWindow()
- {
- MinSize = new Vector2(650, 250);
- Title = Loc.GetString("admin-menu-title");
- RobustXamlLoader.Load(this);
- MasterTabContainer.SetTabTitle(0, Loc.GetString("admin-menu-admin-tab"));
- MasterTabContainer.SetTabTitle(1, Loc.GetString("admin-menu-adminbus-tab"));
- MasterTabContainer.SetTabTitle(2, Loc.GetString("admin-menu-atmos-tab"));
- 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-panic-bunker-tab"));
- /*
- * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
- */
- MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-baby-jail-tab"));
- MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-players-tab"));
- MasterTabContainer.SetTabTitle(8, Loc.GetString("admin-menu-objects-tab"));
- }
+ protected override void Dispose(bool disposing)
+ {
+ OnDisposed?.Invoke();
+ base.Dispose(disposing);
+ OnDisposed = null;
+ }
- protected override void Dispose(bool disposing)
- {
- OnDisposed?.Invoke();
- base.Dispose(disposing);
- OnDisposed = null;
- }
+ private enum TabIndex
+ {
+ Admin = 0,
+ Adminbus,
+ Atmos,
+ Round,
+ Server,
+ PanicBunker,
+ BabyJail,
+ Players,
+ Objects,
}
}
-
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml
index 5d630425abac42..ef679e778d9647 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml
@@ -5,7 +5,7 @@
+ PlaceHolder="{Loc player-list-filter}"/>
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/PlayerActionsWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/PlayerActionsWindow.xaml
index dcc1a05bb54d61..3df57de57b30f6 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/PlayerActionsWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/PlayerActionsWindow.xaml
@@ -4,7 +4,7 @@
Title="{Loc admin-player-actions-window-title}" MinSize="425 272">
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/TeleportWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/TeleportWindow.xaml
index 8f27b17d2dca16..cceb21f14d76bd 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/TeleportWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/TeleportWindow.xaml
@@ -1,9 +1,9 @@
+ Title="{Loc admin-ui-teleport}" MinSize="425 230">
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AdminbusTab/LoadBlueprintsWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminbusTab/LoadBlueprintsWindow.xaml
index 6157a96f42b0dd..e06fc6e8ac5552 100644
--- a/Content.Client/Administration/UI/Tabs/AdminbusTab/LoadBlueprintsWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminbusTab/LoadBlueprintsWindow.xaml
@@ -1,33 +1,33 @@
+ xmlns="https://spacestation14.io" Title="{Loc admin-ui-blueprint-load}">
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml b/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml
index 4a1719cbf8a379..a7c4c35494d9cc 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml
@@ -1,11 +1,11 @@
+ xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-add}">
-
+
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml.cs
index 03fd52f446ae49..72a594469d271c 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/AddAtmosWindow.xaml.cs
@@ -35,7 +35,7 @@ protected override void EnteredTree()
while (query.MoveNext(out var uid, out var grid))
{
_data.Add((uid, grid));
- GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
+ GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
}
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml b/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml
index df1c7aee65843d..6420396788a0b7 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml
@@ -1,31 +1,31 @@
+ xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-add-gas}">
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml.cs
index c06d9161334891..c516acda2a2654 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/AddGasWindow.xaml.cs
@@ -33,7 +33,7 @@ protected override void EnteredTree()
_gridData.Add(entManager.GetNetEntity(uid));
var player = playerManager.LocalEntity;
var playerGrid = entManager.GetComponentOrNull(player)?.GridUid;
- GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
+ GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString("admin-ui-atmos-grid-current") : "")}");
}
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/AtmosTab.xaml b/Content.Client/Administration/UI/Tabs/AtmosTab/AtmosTab.xaml
index 9916972ac161f9..8a80e74c1f4d6e 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/AtmosTab.xaml
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/AtmosTab.xaml
@@ -6,10 +6,10 @@
Margin="4"
MinSize="50 50">
-
-
-
-
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml b/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml
index 242fcf2b82e7bc..f2dba57bff7786 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml
@@ -1,21 +1,21 @@
+ xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-fill-gas}">
-
+
-
+
-
+
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml.cs
index 3353d0873eff35..302ca8f21fd753 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/FillGasWindow.xaml.cs
@@ -36,7 +36,7 @@ protected override void EnteredTree()
{
var player = playerManager.LocalEntity;
var playerGrid = entManager.GetComponentOrNull(player)?.GridUid;
- GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
+ GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
_gridData.Add(entManager.GetNetEntity(uid));
}
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml b/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml
index dbc65772019d76..4102912d693e73 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml
@@ -1,26 +1,26 @@
+ xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-set-temperature}">
-
+
-
+
-
+
-
+
-
+
diff --git a/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml.cs
index 1183efb9b5b62d..b3c4a83ed4124d 100644
--- a/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/AtmosTab/SetTemperatureWindow.xaml.cs
@@ -32,7 +32,7 @@ protected override void EnteredTree()
{
var player = playerManager.LocalEntity;
var playerGrid = entManager.GetComponentOrNull(player)?.GridUid;
- GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
+ GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
_data.Add(entManager.GetNetEntity(uid));
}
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
index ea89916ba8c20b..f4298bbc00f7bb 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
@@ -4,18 +4,17 @@
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
-
-
-
+
+
+
+
-
-
-
+
-
-
-
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
index c8606ca80d5ea1..78eefa34628311 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
@@ -3,6 +3,7 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
@@ -15,17 +16,14 @@ public sealed partial class ObjectsTab : Control
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
- private readonly List _objects = new();
- private readonly List _selections = new();
- private bool _ascending = false; // Set to false for descending order by default
- private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
- public event Action? OnEntryKeyBindDown;
+ private bool _ascending;
+ private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
- private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
- private TimeSpan _nextUpdate;
+ private readonly List _selections = [];
+ public event Action? OnEntryKeyBindDown;
public ObjectsTab()
{
@@ -38,40 +36,25 @@ public ObjectsTab()
RefreshObjectList(_selections[ev.Id]);
};
- foreach (var type in Enum.GetValues(typeof(ObjectsTabSelection)))
+ foreach (var type in Enum.GetValues())
{
- _selections.Add((ObjectsTabSelection)type!);
- ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
+ _selections.Add(type);
+ ObjectTypeOptions.AddItem(GetLocalizedEnumValue(type));
}
ListHeader.OnHeaderClicked += HeaderClicked;
SearchList.SearchBar = SearchLineEdit;
SearchList.GenerateItem += GenerateButton;
SearchList.DataFilterCondition += DataFilterCondition;
+ SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
+ RefreshListButton.OnPressed += _ => RefreshObjectList();
- RefreshObjectList();
- // Set initial selection and refresh the list to apply the initial sort order
var defaultSelection = ObjectsTabSelection.Grids;
- ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection
- RefreshObjectList(defaultSelection); // Refresh the list with the default selection
-
- // Initialize the next update time
- _nextUpdate = TimeSpan.Zero;
- }
-
- protected override void FrameUpdate(FrameEventArgs args)
- {
- base.FrameUpdate(args);
-
- if (_timing.CurTime < _nextUpdate)
- return;
-
- _nextUpdate = _timing.CurTime + _updateFrequency;
-
- RefreshObjectList();
+ ObjectTypeOptions.SelectId((int) defaultSelection);
+ RefreshObjectList(defaultSelection);
}
- private void RefreshObjectList()
+ public void RefreshObjectList()
{
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
}
@@ -101,6 +84,7 @@ private void RefreshObjectList(ObjectsTabSelection selection)
{
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
}
+
break;
}
default:
@@ -111,14 +95,18 @@ private void RefreshObjectList(ObjectsTabSelection selection)
{
var valueA = GetComparableValue(a, _headerClicked);
var valueB = GetComparableValue(b, _headerClicked);
- return _ascending ? Comparer
-
-
+
+
+
+
public void PopulateRecipes()
{
- if (!_entityManager.TryGetComponent(_owner, out var component))
- return;
-
var recipesToShow = new List();
foreach (var recipe in Recipes)
{
@@ -108,21 +105,13 @@ public void PopulateRecipes()
RecipeList.Children.Clear();
foreach (var prototype in sortedRecipesToShow)
{
- List textures;
+ EntityPrototype? recipeProto = null;
if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto) && entityProto != null)
- {
- textures = SpriteComponent.GetPrototypeTextures(entityProto, _resources).Select(o => o.Default).ToList();
- }
- else
- {
- textures = prototype.Icon == null
- ? new List { _spriteSystem.GetPrototypeIcon(prototype.Result).Default }
- : new List { _spriteSystem.Frame0(prototype.Icon) };
- }
+ recipeProto = entityProto;
var canProduce = _lathe.CanProduce(_owner, prototype, quantity);
- var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, textures);
+ var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, recipeProto);
control.OnButtonPressed += s =>
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
@@ -219,14 +208,23 @@ public void UpdateCategories()
///
public void PopulateQueueList(List queue)
{
- QueueList.Clear();
+ QueueList.DisposeAllChildren();
+
var idx = 1;
foreach (var recipe in queue)
{
- var icon = recipe.Icon == null
- ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default
- : _spriteSystem.Frame0(recipe.Icon);
- QueueList.AddItem($"{idx}. {recipe.Name}", icon);
+ var queuedRecipeBox = new BoxContainer();
+ queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
+
+ var queuedRecipeProto = new EntityPrototypeView();
+ queuedRecipeBox.AddChild(queuedRecipeProto);
+ if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null)
+ queuedRecipeProto.SetPrototype(entityProto);
+
+ var queuedRecipeLabel = new Label();
+ queuedRecipeLabel.Text = $"{idx}. {recipe.Name}";
+ queuedRecipeBox.AddChild(queuedRecipeLabel);
+ QueueList.AddChild(queuedRecipeBox);
idx++;
}
}
@@ -236,9 +234,10 @@ public void SetQueueInfo(LatheRecipePrototype? recipe)
FabricatingContainer.Visible = recipe != null;
if (recipe == null)
return;
- Icon.Texture = recipe.Icon == null
- ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default
- : _spriteSystem.Frame0(recipe.Icon);
+
+ if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null)
+ FabricatingEntityProto.SetPrototype(entityProto);
+
NameLabel.Text = $"{recipe.Name}";
}
diff --git a/Content.Client/Lathe/UI/RecipeControl.xaml b/Content.Client/Lathe/UI/RecipeControl.xaml
index d1371a026a22a6..19e20c7c06d3e5 100644
--- a/Content.Client/Lathe/UI/RecipeControl.xaml
+++ b/Content.Client/Lathe/UI/RecipeControl.xaml
@@ -5,14 +5,12 @@
Margin="0"
StyleClasses="ButtonSquare">
-
diff --git a/Content.Client/Lathe/UI/RecipeControl.xaml.cs b/Content.Client/Lathe/UI/RecipeControl.xaml.cs
index 47b6b5932c475d..db428d3cf0e929 100644
--- a/Content.Client/Lathe/UI/RecipeControl.xaml.cs
+++ b/Content.Client/Lathe/UI/RecipeControl.xaml.cs
@@ -4,6 +4,7 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI;
@@ -13,12 +14,13 @@ public sealed partial class RecipeControl : Control
public Action? OnButtonPressed;
public Func TooltipTextSupplier;
- public RecipeControl(LatheRecipePrototype recipe, Func tooltipTextSupplier, bool canProduce, List textures)
+ public RecipeControl(LatheRecipePrototype recipe, Func tooltipTextSupplier, bool canProduce, EntityPrototype? entityPrototype = null)
{
RobustXamlLoader.Load(this);
RecipeName.Text = recipe.Name;
- RecipeTextures.Textures = textures;
+ if (entityPrototype != null)
+ RecipePrototype.SetPrototype(entityPrototype);
Button.Disabled = !canProduce;
TooltipTextSupplier = tooltipTextSupplier;
Button.TooltipSupplier = SupplyTooltip;
diff --git a/Content.Client/Launcher/LauncherConnectingGui.xaml.cs b/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
index ac74ad7b60d21e..5015b710eb48a1 100644
--- a/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
+++ b/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
@@ -116,7 +116,7 @@ private void HandleDisconnectReason(INetStructuredReason? reason)
private void ChangeLoginTip()
{
var tipsDataset = _cfg.GetCVar(CCVars.LoginTipsDataset);
- var loginTipsEnabled = _prototype.TryIndex(tipsDataset, out var tips);
+ var loginTipsEnabled = _prototype.TryIndex(tipsDataset, out var tips);
LoginTips.Visible = loginTipsEnabled;
if (!loginTipsEnabled)
@@ -131,7 +131,7 @@ private void ChangeLoginTip()
var randomIndex = _random.Next(tipList.Count);
var tip = tipList[randomIndex];
- LoginTip.SetMessage(tip);
+ LoginTip.SetMessage(Loc.GetString(tip));
LoginTipTitle.Text = Loc.GetString("connecting-window-tip", ("numberTip", randomIndex));
}
diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
index 11f69165cf6553..ca19d8522c5635 100644
--- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
+++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
@@ -19,6 +19,9 @@ public override void Initialize()
private void OnBehaviorAnimationCompleted(EntityUid uid, LightBehaviourComponent component, AnimationCompletedEvent args)
{
+ if (!args.Finished)
+ return;
+
var container = component.Animations.FirstOrDefault(x => x.FullKey == args.Key);
if (container == null)
diff --git a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
index 842c13dedfe933..5c2c4e4c875bbb 100644
--- a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
+++ b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
@@ -69,6 +69,9 @@ private void OnAfterAutoHandleState(EntityUid uid, RotatingLightComponent comp,
private void OnAnimationComplete(EntityUid uid, RotatingLightComponent comp, AnimationCompletedEvent args)
{
+ if (!args.Finished)
+ return;
+
PlayAnimation(uid, comp);
}
diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
index e8e619ae6dc6e6..b3bc0cafe74139 100644
--- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
+++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
@@ -479,10 +479,10 @@ public void RefreshTraits()
return;
}
- //Setup model
- Dictionary> model = new();
+ // Setup model
+ Dictionary> traitGroups = new();
List defaultTraits = new();
- model.Add("default", defaultTraits);
+ traitGroups.Add(TraitCategoryPrototype.Default, defaultTraits);
foreach (var trait in traits)
{
@@ -492,18 +492,19 @@ public void RefreshTraits()
continue;
}
- if (!model.ContainsKey(trait.Category))
- {
- model.Add(trait.Category, new());
- }
- model[trait.Category].Add(trait.ID);
+ if (!_prototypeManager.HasIndex(trait.Category))
+ continue;
+
+ var group = traitGroups.GetOrNew(trait.Category);
+ group.Add(trait.ID);
}
- //Create UI view from model
- foreach (var (categoryId, traitId) in model)
+ // Create UI view from model
+ foreach (var (categoryId, categoryTraits) in traitGroups)
{
TraitCategoryPrototype? category = null;
- if (categoryId != "default")
+
+ if (categoryId != TraitCategoryPrototype.Default)
{
category = _prototypeManager.Index(categoryId);
// Label
@@ -518,7 +519,7 @@ public void RefreshTraits()
List selectors = new();
var selectionCount = 0;
- foreach (var traitProto in traitId)
+ foreach (var traitProto in categoryTraits)
{
var trait = _prototypeManager.Index(traitProto);
var selector = new TraitPreferenceSelector(trait);
@@ -529,7 +530,15 @@ public void RefreshTraits()
selector.PreferenceChanged += preference =>
{
- Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference);
+ if (preference)
+ {
+ Profile = Profile?.WithTraitPreference(trait.ID, _prototypeManager);
+ }
+ else
+ {
+ Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
+ }
+
SetDirty();
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
};
diff --git a/Content.Client/Lobby/UI/Loadouts/LoadoutWindow.xaml.cs b/Content.Client/Lobby/UI/Loadouts/LoadoutWindow.xaml.cs
index 2737eef1f1ab02..d029eb1223d8df 100644
--- a/Content.Client/Lobby/UI/Loadouts/LoadoutWindow.xaml.cs
+++ b/Content.Client/Lobby/UI/Loadouts/LoadoutWindow.xaml.cs
@@ -29,6 +29,9 @@ public LoadoutWindow(HumanoidCharacterProfile profile, RoleLoadout loadout, Role
if (!protoManager.TryIndex(group, out var groupProto))
continue;
+ if (groupProto.Hidden)
+ continue;
+
var container = new LoadoutGroupContainer(profile, loadout, protoManager.Index(group), session, collection);
LoadoutGroupsContainer.AddTab(container, Loc.GetString(groupProto.Name));
_groups.Add(container);
diff --git a/Content.Client/MouseRotator/MouseRotatorSystem.cs b/Content.Client/MouseRotator/MouseRotatorSystem.cs
index ce174c6144c596..18d60d9a7b9d05 100644
--- a/Content.Client/MouseRotator/MouseRotatorSystem.cs
+++ b/Content.Client/MouseRotator/MouseRotatorSystem.cs
@@ -2,7 +2,6 @@
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
-using Robust.Client.Replays.Loading;
using Robust.Shared.Map;
using Robust.Shared.Timing;
@@ -46,13 +45,19 @@ public override void Update(float frameTime)
// only raise event if the cardinal direction has changed
if (rotator.Simple4DirMode)
{
- var angleDir = angle.GetCardinalDir();
- if (angleDir == curRot.GetCardinalDir())
+ var eyeRot = _eye.CurrentEye.Rotation; // camera rotation
+ var angleDir = (angle + eyeRot).GetCardinalDir(); // apply GetCardinalDir in the camera frame, not in the world frame
+ if (angleDir == (curRot + eyeRot).GetCardinalDir())
return;
- RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
+ var rotation = angleDir.ToAngle() - eyeRot; // convert back to world frame
+ if (rotation >= Math.PI) // convert to [-PI, +PI)
+ rotation -= 2 * Math.PI;
+ else if (rotation < -Math.PI)
+ rotation += 2 * Math.PI;
+ RaisePredictiveEvent(new RequestMouseRotatorRotationEvent
{
- Direction = angleDir,
+ Rotation = rotation
});
return;
diff --git a/Content.Client/Movement/Systems/ContentEyeSystem.cs b/Content.Client/Movement/Systems/ContentEyeSystem.cs
index 182ac92ae0550a..9fbd4b5c37d0b5 100644
--- a/Content.Client/Movement/Systems/ContentEyeSystem.cs
+++ b/Content.Client/Movement/Systems/ContentEyeSystem.cs
@@ -9,7 +9,7 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
- public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, ContentEyeComponent? content = null)
+ public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, bool scalePvs, ContentEyeComponent? content = null)
{
if (!Resolve(uid, ref content, false))
return;
@@ -19,6 +19,14 @@ public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, ContentEy
TargetZoom = zoom,
IgnoreLimit = ignoreLimit,
});
+
+ if (scalePvs)
+ RequestPvsScale(Math.Max(zoom.X, zoom.Y));
+ }
+
+ public void RequestPvsScale(float scale)
+ {
+ RaiseNetworkEvent(new RequestPvsScaleEvent(scale));
}
public void RequestToggleFov()
diff --git a/Content.Client/Options/UI/OptionDropDown.xaml b/Content.Client/Options/UI/OptionDropDown.xaml
new file mode 100644
index 00000000000000..58dcdca6c8bcd6
--- /dev/null
+++ b/Content.Client/Options/UI/OptionDropDown.xaml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/OptionDropDown.xaml.cs b/Content.Client/Options/UI/OptionDropDown.xaml.cs
new file mode 100644
index 00000000000000..506e241a06ed6d
--- /dev/null
+++ b/Content.Client/Options/UI/OptionDropDown.xaml.cs
@@ -0,0 +1,21 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Options.UI;
+
+///
+/// Standard UI control used for drop-downs in the options menu. Intended for use with .
+///
+///
+[GenerateTypedNameReferences]
+public sealed partial class OptionDropDown : Control
+{
+ ///
+ /// The text describing what this drop-down controls.
+ ///
+ public string? Title
+ {
+ get => NameLabel.Text;
+ set => NameLabel.Text = value;
+ }
+}
diff --git a/Content.Client/Options/UI/OptionSlider.xaml b/Content.Client/Options/UI/OptionSlider.xaml
new file mode 100644
index 00000000000000..fa2d78c67ffbbe
--- /dev/null
+++ b/Content.Client/Options/UI/OptionSlider.xaml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/OptionSlider.xaml.cs b/Content.Client/Options/UI/OptionSlider.xaml.cs
new file mode 100644
index 00000000000000..6a377f7ee19262
--- /dev/null
+++ b/Content.Client/Options/UI/OptionSlider.xaml.cs
@@ -0,0 +1,22 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Options.UI;
+
+///
+/// Standard UI control used for sliders in the options menu. Intended for use with .
+///
+///
+///
+[GenerateTypedNameReferences]
+public sealed partial class OptionSlider : Control
+{
+ ///
+ /// The text describing what this slider controls.
+ ///
+ public string? Title
+ {
+ get => NameLabel.Text;
+ set => NameLabel.Text = value;
+ }
+}
diff --git a/Content.Client/Options/UI/OptionsMenu.xaml b/Content.Client/Options/UI/OptionsMenu.xaml
index 4f624c1bb69a68..90486a196ad196 100644
--- a/Content.Client/Options/UI/OptionsMenu.xaml
+++ b/Content.Client/Options/UI/OptionsMenu.xaml
@@ -7,5 +7,6 @@
+
diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs
index 35a3f751bbfe6e..61037f4e4afc4c 100644
--- a/Content.Client/Options/UI/OptionsMenu.xaml.cs
+++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs
@@ -1,9 +1,6 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
-using Robust.Shared.IoC;
-using Content.Client.Options.UI.Tabs;
-
namespace Content.Client.Options.UI
{
@@ -19,13 +16,17 @@ public OptionsMenu()
Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-graphics"));
Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls"));
Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio"));
+ Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-accessibility"));
UpdateTabs();
}
public void UpdateTabs()
{
- GraphicsTab.UpdateProperties();
+ GraphicsTab.Control.ReloadValues();
+ MiscTab.Control.ReloadValues();
+ AccessibilityTab.Control.ReloadValues();
+ AudioTab.Control.ReloadValues();
}
}
}
diff --git a/Content.Client/Options/UI/OptionsTabControlRow.xaml b/Content.Client/Options/UI/OptionsTabControlRow.xaml
new file mode 100644
index 00000000000000..fafdee4df761ee
--- /dev/null
+++ b/Content.Client/Options/UI/OptionsTabControlRow.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/OptionsTabControlRow.xaml.cs b/Content.Client/Options/UI/OptionsTabControlRow.xaml.cs
new file mode 100644
index 00000000000000..31dd9897f4e479
--- /dev/null
+++ b/Content.Client/Options/UI/OptionsTabControlRow.xaml.cs
@@ -0,0 +1,684 @@
+using System.Linq;
+using Content.Client.Stylesheets;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Collections;
+using Robust.Shared.Configuration;
+
+namespace Content.Client.Options.UI;
+
+///
+/// Control used on all tabs of the in-game options menu,
+/// contains the "save" and "reset" buttons and controls the entire logic.
+///
+///
+///
+/// Basic operation is simple: options tabs put this control at the bottom of the tab,
+/// they bind UI controls to it with calls such as ,
+/// then they call . The rest is all handled by the control.
+///
+///
+/// Individual options are implementations of . See the type for details.
+/// Common implementations for building on top of CVars are already exist,
+/// but tabs can define their own if they need to.
+///
+///
+/// Generally, options are added via helper methods such as ,
+/// however it is totally possible to directly instantiate the backing types
+/// and add them via .
+///
+///
+/// The options system is general purpose enough that does not, itself,
+/// know what a CVar is. It does automatically save CVars to config when save is pressed, but otherwise CVar interaction
+/// is handled by implementations.
+///
+///
+/// Behaviorally, the row has 3 control buttons: save, reset changed, and reset to default.
+/// "Save" writes the configuration changes and saves the configuration.
+/// "Reset changed" discards changes made in the menu and re-loads the saved settings.
+/// "Reset to default" resets the settings on the menu to be the default, out-of-the-box values.
+/// Note that "Reset to default" does not save immediately, the user must still press save manually.
+///
+///
+/// The disabled state of the 3 buttons is updated dynamically based on the values of the options.
+///
+///
+[GenerateTypedNameReferences]
+public sealed partial class OptionsTabControlRow : Control
+{
+ [Dependency] private readonly ILocalizationManager _loc = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ private ValueList _options;
+
+ public OptionsTabControlRow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ ResetButton.StyleClasses.Add(StyleBase.ButtonOpenRight);
+ ApplyButton.OnPressed += ApplyButtonPressed;
+ ResetButton.OnPressed += ResetButtonPressed;
+ DefaultButton.OnPressed += DefaultButtonPressed;
+ }
+
+ ///
+ /// Add a new option to be tracked by the control.
+ ///
+ /// The option object that manages this object's logic
+ ///
+ /// The type of option being passed in. Necessary to allow the return type to match the parameter type
+ /// for easy chaining.
+ ///
+ /// The same as passed in, for easy chaining.
+ public T AddOption(T option) where T : BaseOption
+ {
+ _options.Add(option);
+ return option;
+ }
+
+ ///
+ /// Add a checkbox option backed by a simple boolean CVar.
+ ///
+ /// The CVar represented by the checkbox.
+ /// The UI control for the option.
+ ///
+ /// If true, the checkbox is inverted relative to the CVar: if the CVar is true, the checkbox will be unchecked.
+ ///
+ /// The option instance backing the added option.
+ ///
+ public OptionCheckboxCVar AddOptionCheckBox(CVarDef cVar, CheckBox checkBox, bool invert = false)
+ {
+ return AddOption(new OptionCheckboxCVar(this, _cfg, cVar, checkBox, invert));
+ }
+
+ ///
+ /// Add a slider option, displayed in percent, backed by a simple float CVar.
+ ///
+ /// The CVar represented by the slider.
+ /// The UI control for the option.
+ /// The minimum value the slider should allow. The default value represents "0%"
+ /// The maximum value the slider should allow. The default value represents "100%"
+ ///
+ /// Scale with which to multiply slider values when mapped to the backing CVar.
+ /// For example, if a scale of 2 is set, a slider at 75% writes a value of 1.5 to the CVar.
+ ///
+ /// The option instance backing the added option.
+ ///
+ ///
+ /// Note that percentage values are represented as ratios in code, i.e. a value of 100% is "1".
+ ///
+ ///
+ public OptionSliderFloatCVar AddOptionPercentSlider(
+ CVarDef cVar,
+ OptionSlider slider,
+ float min = 0,
+ float max = 1,
+ float scale = 1)
+ {
+ return AddOption(new OptionSliderFloatCVar(this, _cfg, cVar, slider, min, max, scale, FormatPercent));
+ }
+
+ ///
+ /// Add a slider option, backed by a simple integer CVar.
+ ///
+ /// The CVar represented by the slider.
+ /// The UI control for the option.
+ /// The minimum value the slider should allow.
+ /// The maximum value the slider should allow.
+ ///
+ /// An optional delegate used to format the textual value display of the slider.
+ /// If not provided, the default behavior is to directly format the integer value as text.
+ ///
+ /// The option instance backing the added option.
+ public OptionSliderIntCVar AddOptionSlider(
+ CVarDef cVar,
+ OptionSlider slider,
+ int min,
+ int max,
+ Func? format = null)
+ {
+ return AddOption(new OptionSliderIntCVar(this, _cfg, cVar, slider, min, max, format ?? FormatInt));
+ }
+
+ ///
+ /// Add a drop-down option, backed by a CVar.
+ ///
+ /// The CVar represented by the drop-down.
+ /// The UI control for the option.
+ ///
+ /// The set of options that will be shown in the drop-down. Items are ordered as provided.
+ ///
+ /// The type of the CVar being controlled.
+ /// The option instance backing the added option.
+ public OptionDropDownCVar AddOptionDropDown(
+ CVarDef cVar,
+ OptionDropDown dropDown,
+ IReadOnlyCollection.ValueOption> options)
+ where T : notnull
+ {
+ return AddOption(new OptionDropDownCVar(this, _cfg, cVar, dropDown, options));
+ }
+
+ ///
+ /// Initializes the control row. This should be called after all options have been added.
+ ///
+ public void Initialize()
+ {
+ foreach (var option in _options)
+ {
+ option.LoadValue();
+ }
+
+ UpdateButtonState();
+ }
+
+ ///
+ /// Re-loads options in the settings from backing values.
+ /// Should be called when the options window is opened to make sure all values are up-to-date.
+ ///
+ public void ReloadValues()
+ {
+ Initialize();
+ }
+
+ ///
+ /// Called by to signal that an option's value changed through user interaction.
+ ///
+ ///
+ /// implementations should not call this function directly,
+ /// instead they should call .
+ ///
+ public void ValueChanged()
+ {
+ UpdateButtonState();
+ }
+
+ private void UpdateButtonState()
+ {
+ var anyModified = _options.Any(option => option.IsModified());
+ var anyModifiedFromDefault = _options.Any(option => option.IsModifiedFromDefault());
+
+ DefaultButton.Disabled = !anyModifiedFromDefault;
+ ApplyButton.Disabled = !anyModified;
+ ResetButton.Disabled = !anyModified;
+ }
+
+ private void ApplyButtonPressed(BaseButton.ButtonEventArgs obj)
+ {
+ foreach (var option in _options)
+ {
+ if (option.IsModified())
+ option.SaveValue();
+ }
+
+ _cfg.SaveToFile();
+ UpdateButtonState();
+ }
+
+ private void ResetButtonPressed(BaseButton.ButtonEventArgs obj)
+ {
+ foreach (var option in _options)
+ {
+ option.LoadValue();
+ }
+
+ UpdateButtonState();
+ }
+
+ private void DefaultButtonPressed(BaseButton.ButtonEventArgs obj)
+ {
+ foreach (var option in _options)
+ {
+ option.ResetToDefault();
+ }
+
+ UpdateButtonState();
+ }
+
+ private string FormatPercent(OptionSliderFloatCVar slider, float value)
+ {
+ return _loc.GetString("ui-options-value-percent", ("value", value));
+ }
+
+ private static string FormatInt(OptionSliderIntCVar slider, int value)
+ {
+ return value.ToString();
+ }
+}
+
+///
+/// Base class of a single "option" for .
+///
+///
+///
+/// Implementations of this class handle loading values from backing storage or defaults,
+/// handling UI controls, and saving. The main does not know what a CVar is.
+///
+///
+/// is a derived class that makes it easier to work with options
+/// backed by a single CVar.
+///
+///
+/// The control row that owns this option.
+///
+public abstract class BaseOption(OptionsTabControlRow controller)
+{
+ ///
+ /// Should be called by derived implementations to indicate that their value changed, due to user interaction.
+ ///
+ protected virtual void ValueChanged()
+ {
+ controller.ValueChanged();
+ }
+
+ ///
+ /// Loads the value represented by this option from its backing store, into the UI state.
+ ///
+ public abstract void LoadValue();
+
+ ///
+ /// Saves the value in the UI state to the backing store.
+ ///
+ public abstract void SaveValue();
+
+ ///
+ /// Resets the UI state to that of the factory-default value. This should not write to the backing store.
+ ///
+ public abstract void ResetToDefault();
+
+ ///
+ /// Called to check if this option's UI value is different from the backing store value.
+ ///
+ /// If true, the UI value is different and was modified by the user.
+ public abstract bool IsModified();
+
+ ///
+ /// Called to check if this option's UI value is different from the backing store's default value.
+ ///
+ /// If true, the UI value is different.
+ public abstract bool IsModifiedFromDefault();
+}
+
+///
+/// Derived class of intended for making mappings to simple CVars easier.
+///
+/// The type of the CVar.
+///
+public abstract class BaseOptionCVar : BaseOption
+ where TValue : notnull
+{
+ ///
+ /// Raised immediately when the UI value of this option is changed by the user, even before saving.
+ ///
+ ///
+ ///
+ /// This can be used to update parts of the options UI based on the state of a checkbox.
+ ///
+ ///
+ public event Action? ImmediateValueChanged;
+
+ private readonly IConfigurationManager _cfg;
+ private readonly CVarDef _cVar;
+
+ ///
+ /// Sets and gets the actual CVar value to/from the frontend UI state or control.
+ ///
+ ///
+ ///
+ /// In the simplest case, this function should set a UI control's state to represent the CVar,
+ /// and inversely conver the UI control's state to the CVar value. For simple controls like a checkbox or slider,
+ /// this just means passing through their value property.
+ ///
+ ///
+ protected abstract TValue Value { get; set; }
+
+ protected BaseOptionCVar(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CVarDef cVar)
+ : base(controller)
+ {
+ _cfg = cfg;
+ _cVar = cVar;
+ }
+
+ public override void LoadValue()
+ {
+ Value = _cfg.GetCVar(_cVar);
+ }
+
+ public override void SaveValue()
+ {
+ _cfg.SetCVar(_cVar, Value);
+ }
+
+ public override void ResetToDefault()
+ {
+ Value = _cVar.DefaultValue;
+ }
+
+ public override bool IsModified()
+ {
+ return !IsValueEqual(Value, _cfg.GetCVar(_cVar));
+ }
+
+ public override bool IsModifiedFromDefault()
+ {
+ return !IsValueEqual(Value, _cVar.DefaultValue);
+ }
+
+ protected virtual bool IsValueEqual(TValue a, TValue b)
+ {
+ // Use different logic for floats so there's some error margin.
+ // This check is handled cleanly at compile-time by the JIT.
+ if (typeof(TValue) == typeof(float))
+ return MathHelper.CloseToPercent((float) (object) a, (float) (object) b);
+
+ return EqualityComparer.Default.Equals(a, b);
+ }
+
+ protected override void ValueChanged()
+ {
+ base.ValueChanged();
+
+ ImmediateValueChanged?.Invoke(Value);
+ }
+}
+
+///
+/// Implementation of a CVar option that simply corresponds with a .
+///
+///
+///
+/// Generally, you should just call AddOption methods on
+/// instead of instantiating this type directly.
+///
+///
+///
+public sealed class OptionCheckboxCVar : BaseOptionCVar
+{
+ private readonly CheckBox _checkBox;
+ private readonly bool _invert;
+
+ protected override bool Value
+ {
+ get => _checkBox.Pressed ^ _invert;
+ set => _checkBox.Pressed = value ^ _invert;
+ }
+
+ ///
+ /// Creates a new instance of this type.
+ ///
+ /// The control row that owns this option.
+ /// The configuration manager to get and set values from.
+ /// The CVar that is being controlled by this option.
+ /// The UI control for the option.
+ ///
+ /// If true, the checkbox is inverted relative to the CVar: if the CVar is true, the checkbox will be unchecked.
+ ///
+ ///
+ ///
+ /// It is generally more convenient to call overloads on
+ /// such as instead of instantiating this type directly.
+ ///
+ ///
+ public OptionCheckboxCVar(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CVarDef cVar,
+ CheckBox checkBox,
+ bool invert)
+ : base(controller, cfg, cVar)
+ {
+ _checkBox = checkBox;
+ _invert = invert;
+ checkBox.OnToggled += _ =>
+ {
+ ValueChanged();
+ };
+ }
+}
+
+///
+/// Implementation of a CVar option that simply corresponds with a floating-point .
+///
+///
+public sealed class OptionSliderFloatCVar : BaseOptionCVar
+{
+ ///
+ /// Scale with which to multiply slider values when mapped to the backing CVar.
+ ///
+ ///
+ /// For example, if a scale of 2 is set, a slider at 75% writes a value of 1.5 to the CVar.
+ ///
+ public float Scale { get; }
+
+ private readonly OptionSlider _slider;
+ private readonly Func _format;
+
+ protected override float Value
+ {
+ get => _slider.Slider.Value * Scale;
+ set
+ {
+ _slider.Slider.Value = value / Scale;
+ UpdateLabelValue();
+ }
+ }
+
+ ///
+ /// Creates a new instance of this type.
+ ///
+ ///
+ ///
+ /// It is generally more convenient to call overloads on
+ /// such as instead of instantiating this type directly.
+ ///
+ ///
+ /// The control row that owns this option.
+ /// The configuration manager to get and set values from.
+ /// The CVar that is being controlled by this option.
+ /// The UI control for the option.
+ /// The minimum value the slider should allow.
+ /// The maximum value the slider should allow.
+ ///
+ /// Scale with which to multiply slider values when mapped to the backing CVar. See .
+ ///
+ /// Function that will be called to format the value display next to the slider.
+ public OptionSliderFloatCVar(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CVarDef cVar,
+ OptionSlider slider,
+ float minValue,
+ float maxValue,
+ float scale,
+ Func format) : base(controller, cfg, cVar)
+ {
+ Scale = scale;
+ _slider = slider;
+ _format = format;
+
+ slider.Slider.MinValue = minValue;
+ slider.Slider.MaxValue = maxValue;
+
+ slider.Slider.OnValueChanged += _ =>
+ {
+ ValueChanged();
+ UpdateLabelValue();
+ };
+ }
+
+ private void UpdateLabelValue()
+ {
+ _slider.ValueLabel.Text = _format(this, _slider.Slider.Value);
+ }
+}
+
+///
+/// Implementation of a CVar option that simply corresponds with an integer .
+///
+///
+public sealed class OptionSliderIntCVar : BaseOptionCVar
+{
+ private readonly OptionSlider _slider;
+ private readonly Func _format;
+
+ protected override int Value
+ {
+ get => (int) _slider.Slider.Value;
+ set
+ {
+ _slider.Slider.Value = value;
+ UpdateLabelValue();
+ }
+ }
+
+ ///
+ /// Creates a new instance of this type.
+ ///
+ ///
+ ///
+ /// It is generally more convenient to call overloads on
+ /// such as instead of instantiating this type directly.
+ ///
+ ///
+ /// The control row that owns this option.
+ /// The configuration manager to get and set values from.
+ /// The CVar that is being controlled by this option.
+ /// The UI control for the option.
+ /// The minimum value the slider should allow.
+ /// The maximum value the slider should allow.
+ /// Function that will be called to format the value display next to the slider.
+ public OptionSliderIntCVar(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CVarDef cVar,
+ OptionSlider slider,
+ int minValue,
+ int maxValue,
+ Func format) : base(controller, cfg, cVar)
+ {
+ _slider = slider;
+ _format = format;
+
+ slider.Slider.MinValue = minValue;
+ slider.Slider.MaxValue = maxValue;
+ slider.Slider.Rounded = true;
+
+ slider.Slider.OnValueChanged += _ =>
+ {
+ ValueChanged();
+ UpdateLabelValue();
+ };
+ }
+
+ private void UpdateLabelValue()
+ {
+ _slider.ValueLabel.Text = _format(this, (int) _slider.Slider.Value);
+ }
+}
+
+///
+/// Implementation of a CVar option via a drop-down.
+///
+///
+public sealed class OptionDropDownCVar : BaseOptionCVar where T : notnull
+{
+ private readonly OptionDropDown _dropDown;
+ private readonly ItemEntry[] _entries;
+
+ protected override T Value
+ {
+ get => (T) _dropDown.Button.SelectedMetadata!;
+ set => _dropDown.Button.SelectId(FindValueId(value));
+ }
+
+ ///
+ /// Creates a new instance of this type.
+ ///
+ ///
+ ///
+ /// It is generally more convenient to call overloads on
+ /// such as instead of instantiating this type directly.
+ ///
+ ///
+ /// The control row that owns this option.
+ /// The configuration manager to get and set values from.
+ /// The CVar that is being controlled by this option.
+ /// The UI control for the option.
+ /// The list of options shown to the user.
+ public OptionDropDownCVar(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CVarDef cVar,
+ OptionDropDown dropDown,
+ IReadOnlyCollection options) : base(controller, cfg, cVar)
+ {
+ if (options.Count == 0)
+ throw new ArgumentException("Need at least one option!");
+
+ _dropDown = dropDown;
+ _entries = new ItemEntry[options.Count];
+
+ var button = dropDown.Button;
+ var i = 0;
+ foreach (var option in options)
+ {
+ _entries[i] = new ItemEntry
+ {
+ Key = option.Key,
+ };
+
+ button.AddItem(option.Label, i);
+ button.SetItemMetadata(button.GetIdx(i), option.Key);
+ i += 1;
+ }
+
+ dropDown.Button.OnItemSelected += args =>
+ {
+ dropDown.Button.SelectId(args.Id);
+ ValueChanged();
+ };
+ }
+
+ private int FindValueId(T value)
+ {
+ for (var i = 0; i < _entries.Length; i++)
+ {
+ if (IsValueEqual(_entries[i].Key, value))
+ return i;
+ }
+
+ // This will just default select the first entry or whatever.
+ return 0;
+ }
+
+ ///
+ /// A single option for a drop-down.
+ ///
+ /// The value that this option has. This is what will be written to the CVar if selected.
+ /// The visual text shown to the user for the option.
+ ///
+ ///
+ public sealed class ValueOption(T key, string label)
+ {
+ ///
+ /// The value that this option has. This is what will be written to the CVar if selected.
+ ///
+ public readonly T Key = key;
+
+ ///
+ /// The visual text shown to the user for the option.
+ ///
+ public readonly string Label = label;
+ }
+
+ private struct ItemEntry
+ {
+ public T Key;
+ }
+}
diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
new file mode 100644
index 00000000000000..54d92b2b11c88b
--- /dev/null
+++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
new file mode 100644
index 00000000000000..15182fbf126b8a
--- /dev/null
+++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
@@ -0,0 +1,24 @@
+using Content.Shared.CCVar;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Options.UI.Tabs;
+
+[GenerateTypedNameReferences]
+public sealed partial class AccessibilityTab : Control
+{
+ public AccessibilityTab()
+ {
+ RobustXamlLoader.Load(this);
+
+ Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox);
+ Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox);
+ Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
+ Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
+ Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
+
+ Control.Initialize();
+ }
+}
+
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml b/Content.Client/Options/UI/Tabs/AudioTab.xaml
index e54b0dc34ee845..c374af31c588fb 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml
@@ -1,128 +1,26 @@
+ xmlns:ui="clr-namespace:Content.Client.Options.UI">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
index 470ca7d799d8f6..78186d446c7625 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
@@ -3,200 +3,72 @@
using Robust.Client.Audio;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Configuration;
-using Range = Robust.Client.UserInterface.Controls.Range;
-namespace Content.Client.Options.UI.Tabs
-{
- [GenerateTypedNameReferences]
- public sealed partial class AudioTab : Control
- {
- [Dependency] private readonly IConfigurationManager _cfg = default!;
- private readonly IAudioManager _audio;
-
- public AudioTab()
- {
- RobustXamlLoader.Load(this);
- IoCManager.InjectDependencies(this);
-
- _audio = IoCManager.Resolve();
- LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled);
- RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
- EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
- AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled);
-
- ApplyButton.OnPressed += OnApplyButtonPressed;
- ResetButton.OnPressed += OnResetButtonPressed;
- MasterVolumeSlider.OnValueChanged += OnMasterVolumeSliderChanged;
- MidiVolumeSlider.OnValueChanged += OnMidiVolumeSliderChanged;
- AmbientMusicVolumeSlider.OnValueChanged += OnAmbientMusicVolumeSliderChanged;
- AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged;
- AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged;
- LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
- InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
- LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
- RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
- EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
- AdminSoundsCheckBox.OnToggled += OnAdminSoundsCheckToggled;
-
- AmbienceSoundsSlider.MinValue = _cfg.GetCVar(CCVars.MinMaxAmbientSourcesConfigured);
- AmbienceSoundsSlider.MaxValue = _cfg.GetCVar(CCVars.MaxMaxAmbientSourcesConfigured);
-
- Reset();
- }
-
- protected override void Dispose(bool disposing)
- {
- ApplyButton.OnPressed -= OnApplyButtonPressed;
- ResetButton.OnPressed -= OnResetButtonPressed;
- MasterVolumeSlider.OnValueChanged -= OnMasterVolumeSliderChanged;
- MidiVolumeSlider.OnValueChanged -= OnMidiVolumeSliderChanged;
- AmbientMusicVolumeSlider.OnValueChanged -= OnAmbientMusicVolumeSliderChanged;
- AmbienceVolumeSlider.OnValueChanged -= OnAmbienceVolumeSliderChanged;
- LobbyVolumeSlider.OnValueChanged -= OnLobbyVolumeSliderChanged;
- InterfaceVolumeSlider.OnValueChanged -= OnInterfaceVolumeSliderChanged;
- base.Dispose(disposing);
- }
-
- private void OnLobbyVolumeSliderChanged(Range obj)
- {
- UpdateChanges();
- }
-
- private void OnInterfaceVolumeSliderChanged(Range obj)
- {
- UpdateChanges();
- }
-
- private void OnAmbientMusicVolumeSliderChanged(Range obj)
- {
- UpdateChanges();
- }
-
- private void OnAmbienceVolumeSliderChanged(Range obj)
- {
- UpdateChanges();
- }
-
- private void OnAmbienceSoundsSliderChanged(Range obj)
- {
- UpdateChanges();
- }
-
- private void OnMasterVolumeSliderChanged(Range range)
- {
- _audio.SetMasterGain(MasterVolumeSlider.Value / 100f * ContentAudioSystem.MasterVolumeMultiplier);
- UpdateChanges();
- }
+namespace Content.Client.Options.UI.Tabs;
- private void OnMidiVolumeSliderChanged(Range range)
- {
- UpdateChanges();
- }
-
- private void OnLobbyMusicCheckToggled(BaseButton.ButtonEventArgs args)
- {
- UpdateChanges();
- }
- private void OnRestartSoundsCheckToggled(BaseButton.ButtonEventArgs args)
- {
- UpdateChanges();
- }
- private void OnEventMusicCheckToggled(BaseButton.ButtonEventArgs args)
- {
- UpdateChanges();
- }
-
- private void OnAdminSoundsCheckToggled(BaseButton.ButtonEventArgs args)
- {
- UpdateChanges();
- }
-
- private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
- {
- _cfg.SetCVar(CVars.AudioMasterVolume, MasterVolumeSlider.Value / 100f * ContentAudioSystem.MasterVolumeMultiplier);
- // Want the CVar updated values to have the multiplier applied
- // For the UI we just display 0-100 still elsewhere
- _cfg.SetCVar(CVars.MidiVolume, MidiVolumeSlider.Value / 100f * ContentAudioSystem.MidiVolumeMultiplier);
- _cfg.SetCVar(CCVars.AmbienceVolume, AmbienceVolumeSlider.Value / 100f * ContentAudioSystem.AmbienceMultiplier);
- _cfg.SetCVar(CCVars.AmbientMusicVolume, AmbientMusicVolumeSlider.Value / 100f * ContentAudioSystem.AmbientMusicMultiplier);
- _cfg.SetCVar(CCVars.LobbyMusicVolume, LobbyVolumeSlider.Value / 100f * ContentAudioSystem.LobbyMultiplier);
- _cfg.SetCVar(CCVars.InterfaceVolume, InterfaceVolumeSlider.Value / 100f * ContentAudioSystem.InterfaceMultiplier);
-
- _cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value);
-
- _cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed);
- _cfg.SetCVar(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox.Pressed);
- _cfg.SetCVar(CCVars.EventMusicEnabled, EventMusicCheckBox.Pressed);
- _cfg.SetCVar(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox.Pressed);
- _cfg.SaveToFile();
- UpdateChanges();
- }
-
- private void OnResetButtonPressed(BaseButton.ButtonEventArgs args)
- {
- Reset();
- }
-
- private void Reset()
- {
- MasterVolumeSlider.Value = _cfg.GetCVar(CVars.AudioMasterVolume) * 100f / ContentAudioSystem.MasterVolumeMultiplier;
- MidiVolumeSlider.Value = _cfg.GetCVar(CVars.MidiVolume) * 100f / ContentAudioSystem.MidiVolumeMultiplier;
- AmbienceVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbienceVolume) * 100f / ContentAudioSystem.AmbienceMultiplier;
- AmbientMusicVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier;
- LobbyVolumeSlider.Value = _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier;
- InterfaceVolumeSlider.Value = _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier;
-
- AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources);
-
- LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled);
- RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
- EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
- AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled);
- UpdateChanges();
- }
+[GenerateTypedNameReferences]
+public sealed partial class AudioTab : Control
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IAudioManager _audio = default!;
- private void UpdateChanges()
- {
- // y'all need jesus.
- var isMasterVolumeSame =
- Math.Abs(MasterVolumeSlider.Value - _cfg.GetCVar(CVars.AudioMasterVolume) * 100f / ContentAudioSystem.MasterVolumeMultiplier) < 0.01f;
- var isMidiVolumeSame =
- Math.Abs(MidiVolumeSlider.Value - _cfg.GetCVar(CVars.MidiVolume) * 100f / ContentAudioSystem.MidiVolumeMultiplier) < 0.01f;
- var isAmbientVolumeSame =
- Math.Abs(AmbienceVolumeSlider.Value - _cfg.GetCVar(CCVars.AmbienceVolume) * 100f / ContentAudioSystem.AmbienceMultiplier) < 0.01f;
- var isAmbientMusicVolumeSame =
- Math.Abs(AmbientMusicVolumeSlider.Value - _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier) < 0.01f;
- var isLobbyVolumeSame =
- Math.Abs(LobbyVolumeSlider.Value - _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier) < 0.01f;
- var isInterfaceVolumeSame =
- Math.Abs(InterfaceVolumeSlider.Value - _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier) < 0.01f;
+ public AudioTab()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ var masterVolume = Control.AddOptionPercentSlider(
+ CVars.AudioMasterVolume,
+ SliderVolumeMaster,
+ scale: ContentAudioSystem.MasterVolumeMultiplier);
+ masterVolume.ImmediateValueChanged += OnMasterVolumeSliderChanged;
+
+ Control.AddOptionPercentSlider(
+ CVars.MidiVolume,
+ SliderVolumeMidi,
+ scale: ContentAudioSystem.MidiVolumeMultiplier);
+
+ Control.AddOptionPercentSlider(
+ CCVars.AmbientMusicVolume,
+ SliderVolumeAmbientMusic,
+ scale: ContentAudioSystem.AmbientMusicMultiplier);
+
+ Control.AddOptionPercentSlider(
+ CCVars.AmbienceVolume,
+ SliderVolumeAmbience,
+ scale: ContentAudioSystem.AmbienceMultiplier);
+
+ Control.AddOptionPercentSlider(
+ CCVars.LobbyMusicVolume,
+ SliderVolumeLobby,
+ scale: ContentAudioSystem.LobbyMultiplier);
+
+ Control.AddOptionPercentSlider(
+ CCVars.InterfaceVolume,
+ SliderVolumeInterface,
+ scale: ContentAudioSystem.InterfaceMultiplier);
+
+ Control.AddOptionSlider(
+ CCVars.MaxAmbientSources,
+ SliderMaxAmbienceSounds,
+ _cfg.GetCVar(CCVars.MinMaxAmbientSourcesConfigured),
+ _cfg.GetCVar(CCVars.MaxMaxAmbientSourcesConfigured));
+
+ Control.AddOptionCheckBox(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox);
+ Control.AddOptionCheckBox(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox);
+ Control.AddOptionCheckBox(CCVars.EventMusicEnabled, EventMusicCheckBox);
+ Control.AddOptionCheckBox(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox);
+
+ Control.Initialize();
+ }
- var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources);
- var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled);
- var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
- var isEventSame = EventMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.EventMusicEnabled);
- var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled);
- var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
- && isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
- ApplyButton.Disabled = isEverythingSame;
- ResetButton.Disabled = isEverythingSame;
- MasterVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", MasterVolumeSlider.Value / 100));
- MidiVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", MidiVolumeSlider.Value / 100));
- AmbientMusicVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", AmbientMusicVolumeSlider.Value / 100));
- AmbienceVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", AmbienceVolumeSlider.Value / 100));
- LobbyVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", LobbyVolumeSlider.Value / 100));
- InterfaceVolumeLabel.Text =
- Loc.GetString("ui-options-volume-percent", ("volume", InterfaceVolumeSlider.Value / 100));
- AmbienceSoundsLabel.Text = ((int)AmbienceSoundsSlider.Value).ToString();
- }
+ private void OnMasterVolumeSliderChanged(float value)
+ {
+ // TODO: I was thinking of giving OptionsTabControlRow a flag to "set CVar immediately", but I'm deferring that
+ // until there's a proper system for enforcing people don't close the window with pending changes.
+ _audio.SetMasterGain(value);
}
}
diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml
index ec1b9aa002f8b0..f1b9743cad385b 100644
--- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml
+++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml
@@ -1,53 +1,38 @@
+ xmlns:tabs="clr-namespace:Content.Client.Options.UI.Tabs"
+ xmlns:ui="clr-namespace:Content.Client.Options.UI">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs
index a22adf3e632960..f53a2edd95793b 100644
--- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs
@@ -7,220 +7,141 @@
using Robust.Shared;
using Robust.Shared.Configuration;
-namespace Content.Client.Options.UI.Tabs
+namespace Content.Client.Options.UI.Tabs;
+
+[GenerateTypedNameReferences]
+public sealed partial class GraphicsTab : Control
{
- [GenerateTypedNameReferences]
- public sealed partial class GraphicsTab : Control
- {
- private static readonly float[] UIScaleOptions =
- {
- 0f,
- 0.75f,
- 1f,
- 1.25f,
- 1.50f,
- 1.75f,
- 2f
- };
-
- [Dependency] private readonly IConfigurationManager _cfg = default!;
-
- public GraphicsTab()
- {
- IoCManager.InjectDependencies(this);
- RobustXamlLoader.Load(this);
-
- VSyncCheckBox.OnToggled += OnCheckBoxToggled;
- FullscreenCheckBox.OnToggled += OnCheckBoxToggled;
-
- LightingPresetOption.AddItem(Loc.GetString("ui-options-lighting-very-low"));
- LightingPresetOption.AddItem(Loc.GetString("ui-options-lighting-low"));
- LightingPresetOption.AddItem(Loc.GetString("ui-options-lighting-medium"));
- LightingPresetOption.AddItem(Loc.GetString("ui-options-lighting-high"));
- LightingPresetOption.OnItemSelected += OnLightingQualityChanged;
-
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-auto",
- ("scale", UserInterfaceManager.DefaultUIScale)));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-75"));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-100"));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-125"));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-150"));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-175"));
- UIScaleOption.AddItem(Loc.GetString("ui-options-scale-200"));
- UIScaleOption.OnItemSelected += OnUIScaleChanged;
-
- ViewportStretchCheckBox.OnToggled += _ =>
- {
- UpdateViewportScale();
- UpdateApplyButton();
- };
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
- ViewportScaleSlider.OnValueChanged += _ =>
- {
- UpdateApplyButton();
- UpdateViewportScale();
- };
+ public GraphicsTab()
+ {
+ IoCManager.InjectDependencies(this);
+ RobustXamlLoader.Load(this);
+
+ Control.AddOptionCheckBox(CVars.DisplayVSync, VSyncCheckBox);
+ Control.AddOption(new OptionFullscreen(Control, _cfg, FullscreenCheckBox));
+ Control.AddOption(new OptionLightingQuality(Control, _cfg, DropDownLightingQuality));
+
+ Control.AddOptionDropDown(
+ CVars.DisplayUIScale,
+ DropDownUIScale,
+ [
+ new OptionDropDownCVar.ValueOption(
+ 0f,
+ Loc.GetString("ui-options-scale-auto", ("scale", UserInterfaceManager.DefaultUIScale))),
+ new OptionDropDownCVar.ValueOption(0.75f, Loc.GetString("ui-options-scale-75")),
+ new OptionDropDownCVar.ValueOption(1.00f, Loc.GetString("ui-options-scale-100")),
+ new OptionDropDownCVar.ValueOption(1.25f, Loc.GetString("ui-options-scale-125")),
+ new OptionDropDownCVar.ValueOption(1.50f, Loc.GetString("ui-options-scale-150")),
+ new OptionDropDownCVar.ValueOption(1.75f, Loc.GetString("ui-options-scale-175")),
+ new OptionDropDownCVar.ValueOption(2.00f, Loc.GetString("ui-options-scale-200")),
+ ]);
+
+ var vpStretch = Control.AddOptionCheckBox(CCVars.ViewportStretch, ViewportStretchCheckBox);
+ var vpVertFit = Control.AddOptionCheckBox(CCVars.ViewportVerticalFit, ViewportVerticalFitCheckBox);
+ Control.AddOptionSlider(
+ CCVars.ViewportFixedScaleFactor,
+ ViewportScaleSlider,
+ 1,
+ 5,
+ (_, value) => Loc.GetString("ui-options-vp-scale-value", ("scale", value)));
+
+ vpStretch.ImmediateValueChanged += _ => UpdateViewportSettingsVisibility();
+ vpVertFit.ImmediateValueChanged += _ => UpdateViewportSettingsVisibility();
+
+ Control.AddOptionSlider(
+ CCVars.ViewportWidth,
+ ViewportWidthSlider,
+ (int)ViewportWidthSlider.Slider.MinValue,
+ (int)ViewportWidthSlider.Slider.MaxValue);
+
+ Control.AddOption(new OptionIntegerScaling(Control, _cfg, IntegerScalingCheckBox));
+ Control.AddOptionCheckBox(CCVars.ViewportScaleRender, ViewportLowResCheckBox, invert: true);
+ Control.AddOptionCheckBox(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox);
+ Control.AddOptionCheckBox(CCVars.HudFpsCounterVisible, FpsCounterCheckBox);
+
+ Control.Initialize();
+
+ _cfg.OnValueChanged(CCVars.ViewportMinimumWidth, _ => UpdateViewportWidthRange());
+ _cfg.OnValueChanged(CCVars.ViewportMaximumWidth, _ => UpdateViewportWidthRange());
+
+ UpdateViewportWidthRange();
+ UpdateViewportSettingsVisibility();
+ }
- ViewportWidthSlider.OnValueChanged += _ =>
- {
- UpdateViewportWidthDisplay();
- UpdateApplyButton();
- };
+ private void UpdateViewportSettingsVisibility()
+ {
+ ViewportScaleSlider.Visible = !ViewportStretchCheckBox.Pressed;
+ IntegerScalingCheckBox.Visible = ViewportStretchCheckBox.Pressed;
+ ViewportVerticalFitCheckBox.Visible = ViewportStretchCheckBox.Pressed;
+ ViewportWidthSlider.Visible = !ViewportStretchCheckBox.Pressed || !ViewportVerticalFitCheckBox.Pressed;
+ }
- ViewportVerticalFitCheckBox.OnToggled += _ =>
- {
- UpdateViewportScale();
- UpdateApplyButton();
- };
+ private void UpdateViewportWidthRange()
+ {
+ var min = _cfg.GetCVar(CCVars.ViewportMinimumWidth);
+ var max = _cfg.GetCVar(CCVars.ViewportMaximumWidth);
- IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled;
- ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
- ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled;
- FpsCounterCheckBox.OnToggled += OnCheckBoxToggled;
- ApplyButton.OnPressed += OnApplyButtonPressed;
- VSyncCheckBox.Pressed = _cfg.GetCVar(CVars.DisplayVSync);
- FullscreenCheckBox.Pressed = ConfigIsFullscreen;
- LightingPresetOption.SelectId(GetConfigLightingQuality());
- UIScaleOption.SelectId(GetConfigUIScalePreset(ConfigUIScale));
- ViewportScaleSlider.Value = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
- ViewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch);
- IntegerScalingCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0;
- ViewportVerticalFitCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportVerticalFit);
- ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender);
- ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality);
- FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible);
- ViewportWidthSlider.Value = _cfg.GetCVar(CCVars.ViewportWidth);
-
- _cfg.OnValueChanged(CCVars.ViewportMinimumWidth, _ => UpdateViewportWidthRange());
- _cfg.OnValueChanged(CCVars.ViewportMaximumWidth, _ => UpdateViewportWidthRange());
-
- UpdateViewportWidthRange();
- UpdateViewportWidthDisplay();
- UpdateViewportScale();
- UpdateApplyButton();
- }
+ ViewportWidthSlider.Slider.MinValue = min;
+ ViewportWidthSlider.Slider.MaxValue = max;
+ }
- private void OnUIScaleChanged(OptionButton.ItemSelectedEventArgs args)
- {
- UIScaleOption.SelectId(args.Id);
- UpdateApplyButton();
- }
+ private sealed class OptionLightingQuality : BaseOption
+ {
+ private readonly IConfigurationManager _cfg;
+ private readonly OptionDropDown _dropDown;
- private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
- {
- _cfg.SetCVar(CVars.DisplayVSync, VSyncCheckBox.Pressed);
- SetConfigLightingQuality(LightingPresetOption.SelectedId);
-
- _cfg.SetCVar(CVars.DisplayWindowMode,
- (int) (FullscreenCheckBox.Pressed ? WindowMode.Fullscreen : WindowMode.Windowed));
- _cfg.SetCVar(CVars.DisplayUIScale, UIScaleOptions[UIScaleOption.SelectedId]);
- _cfg.SetCVar(CCVars.ViewportStretch, ViewportStretchCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ViewportFixedScaleFactor, (int) ViewportScaleSlider.Value);
- _cfg.SetCVar(CCVars.ViewportSnapToleranceMargin,
- IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0);
- _cfg.SetCVar(CCVars.ViewportVerticalFit, ViewportVerticalFitCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed);
- _cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ViewportWidth, (int) ViewportWidthSlider.Value);
-
- _cfg.SaveToFile();
- UpdateApplyButton();
- }
+ private const int QualityVeryLow = 0;
+ private const int QualityLow = 1;
+ private const int QualityMedium = 2;
+ private const int QualityHigh = 3;
- private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
- {
- UpdateApplyButton();
- }
+ private const int QualityDefault = QualityMedium;
- private void OnLightingQualityChanged(OptionButton.ItemSelectedEventArgs args)
+ public OptionLightingQuality(OptionsTabControlRow controller, IConfigurationManager cfg, OptionDropDown dropDown) : base(controller)
{
- LightingPresetOption.SelectId(args.Id);
- UpdateApplyButton();
+ _cfg = cfg;
+ _dropDown = dropDown;
+ var button = dropDown.Button;
+ button.AddItem(Loc.GetString("ui-options-lighting-very-low"), QualityVeryLow);
+ button.AddItem(Loc.GetString("ui-options-lighting-low"), QualityLow);
+ button.AddItem(Loc.GetString("ui-options-lighting-medium"), QualityMedium);
+ button.AddItem(Loc.GetString("ui-options-lighting-high"), QualityHigh);
+ button.OnItemSelected += OnOptionSelected;
}
- private void UpdateApplyButton()
+ private void OnOptionSelected(OptionButton.ItemSelectedEventArgs obj)
{
- var isVSyncSame = VSyncCheckBox.Pressed == _cfg.GetCVar(CVars.DisplayVSync);
- var isFullscreenSame = FullscreenCheckBox.Pressed == ConfigIsFullscreen;
- var isLightingQualitySame = LightingPresetOption.SelectedId == GetConfigLightingQuality();
- var isUIScaleSame = MathHelper.CloseToPercent(UIScaleOptions[UIScaleOption.SelectedId], ConfigUIScale);
- var isVPStretchSame = ViewportStretchCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportStretch);
- var isVPScaleSame = (int) ViewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
- var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0);
- var isVPVerticalFitSame = ViewportVerticalFitCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportVerticalFit);
- var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender);
- var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality);
- var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible);
- var isWidthSame = (int) ViewportWidthSlider.Value == _cfg.GetCVar(CCVars.ViewportWidth);
-
- ApplyButton.Disabled = isVSyncSame &&
- isFullscreenSame &&
- isLightingQualitySame &&
- isUIScaleSame &&
- isVPStretchSame &&
- isVPScaleSame &&
- isIntegerScalingSame &&
- isVPVerticalFitSame &&
- isVPResSame &&
- isPLQSame &&
- isFpsCounterVisibleSame &&
- isWidthSame;
+ _dropDown.Button.SelectId(obj.Id);
+ ValueChanged();
}
- private bool ConfigIsFullscreen =>
- _cfg.GetCVar(CVars.DisplayWindowMode) == (int) WindowMode.Fullscreen;
-
- public void UpdateProperties()
+ public override void LoadValue()
{
- FullscreenCheckBox.Pressed = ConfigIsFullscreen;
+ _dropDown.Button.SelectId(GetConfigLightingQuality());
}
-
- private float ConfigUIScale => _cfg.GetCVar(CVars.DisplayUIScale);
-
- private int GetConfigLightingQuality()
+ public override void SaveValue()
{
- var val = _cfg.GetCVar(CVars.LightResolutionScale);
- var soft = _cfg.GetCVar(CVars.LightSoftShadows);
- if (val <= 0.125)
- {
- return 0;
- }
- else if ((val <= 0.5) && !soft)
- {
- return 1;
- }
- else if (val <= 0.5)
+ switch (_dropDown.Button.SelectedId)
{
- return 2;
- }
- else
- {
- return 3;
- }
- }
-
- private void SetConfigLightingQuality(int value)
- {
- switch (value)
- {
- case 0:
+ case QualityVeryLow:
_cfg.SetCVar(CVars.LightResolutionScale, 0.125f);
_cfg.SetCVar(CVars.LightSoftShadows, false);
_cfg.SetCVar(CVars.LightBlur, false);
break;
- case 1:
+ case QualityLow:
_cfg.SetCVar(CVars.LightResolutionScale, 0.5f);
_cfg.SetCVar(CVars.LightSoftShadows, false);
_cfg.SetCVar(CVars.LightBlur, true);
break;
- case 2:
+ default: // = QualityMedium
_cfg.SetCVar(CVars.LightResolutionScale, 0.5f);
_cfg.SetCVar(CVars.LightSoftShadows, true);
_cfg.SetCVar(CVars.LightBlur, true);
break;
- case 3:
+ case QualityHigh:
_cfg.SetCVar(CVars.LightResolutionScale, 1);
_cfg.SetCVar(CVars.LightSoftShadows, true);
_cfg.SetCVar(CVars.LightBlur, true);
@@ -228,40 +149,83 @@ private void SetConfigLightingQuality(int value)
}
}
- private static int GetConfigUIScalePreset(float value)
+ public override void ResetToDefault()
{
- for (var i = 0; i < UIScaleOptions.Length; i++)
- {
- if (MathHelper.CloseToPercent(UIScaleOptions[i], value))
- {
- return i;
- }
- }
+ _dropDown.Button.SelectId(QualityDefault);
+ }
+
+ public override bool IsModified()
+ {
+ return _dropDown.Button.SelectedId != GetConfigLightingQuality();
+ }
+
+ public override bool IsModifiedFromDefault()
+ {
+ return _dropDown.Button.SelectedId != QualityDefault;
+ }
+
+ private int GetConfigLightingQuality()
+ {
+ var val = _cfg.GetCVar(CVars.LightResolutionScale);
+ var soft = _cfg.GetCVar(CVars.LightSoftShadows);
+ if (val <= 0.125)
+ return QualityVeryLow;
+
+ if ((val <= 0.5) && !soft)
+ return QualityLow;
- return 0;
+ if (val <= 0.5)
+ return QualityMedium;
+
+ return QualityHigh;
}
+ }
+
+ private sealed class OptionFullscreen : BaseOptionCVar
+ {
+ private readonly CheckBox _checkBox;
- private void UpdateViewportScale()
+ protected override int Value
{
- ViewportScaleBox.Visible = !ViewportStretchCheckBox.Pressed;
- IntegerScalingCheckBox.Visible = ViewportStretchCheckBox.Pressed;
- ViewportVerticalFitCheckBox.Visible = ViewportStretchCheckBox.Pressed;
- ViewportWidthSlider.Visible = ViewportWidthSliderDisplay.Visible = !ViewportStretchCheckBox.Pressed || ViewportStretchCheckBox.Pressed && !ViewportVerticalFitCheckBox.Pressed;
- ViewportScaleText.Text = Loc.GetString("ui-options-vp-scale", ("scale", ViewportScaleSlider.Value));
+ get => _checkBox.Pressed ? (int) WindowMode.Fullscreen : (int) WindowMode.Windowed;
+ set => _checkBox.Pressed = (value == (int) WindowMode.Fullscreen);
}
- private void UpdateViewportWidthRange()
+ public OptionFullscreen(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CheckBox checkBox)
+ : base(controller, cfg, CVars.DisplayWindowMode)
{
- var min = _cfg.GetCVar(CCVars.ViewportMinimumWidth);
- var max = _cfg.GetCVar(CCVars.ViewportMaximumWidth);
+ _checkBox = checkBox;
+ _checkBox.OnToggled += _ =>
+ {
+ ValueChanged();
+ };
+ }
+ }
- ViewportWidthSlider.MinValue = min;
- ViewportWidthSlider.MaxValue = max;
+ private sealed class OptionIntegerScaling : BaseOptionCVar
+ {
+ private readonly CheckBox _checkBox;
+
+ protected override int Value
+ {
+ get => _checkBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0;
+ set => _checkBox.Pressed = (value != 0);
}
- private void UpdateViewportWidthDisplay()
+ public OptionIntegerScaling(
+ OptionsTabControlRow controller,
+ IConfigurationManager cfg,
+ CheckBox checkBox)
+ : base(controller, cfg, CCVars.ViewportSnapToleranceMargin)
{
- ViewportWidthSliderDisplay.Text = Loc.GetString("ui-options-vp-width", ("width", (int) ViewportWidthSlider.Value));
+ _checkBox = checkBox;
+ _checkBox.OnToggled += _ =>
+ {
+ ValueChanged();
+ };
}
}
}
diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml b/Content.Client/Options/UI/Tabs/MiscTab.xaml
index 0c6ec3804245da..c1733e209dbe7f 100644
--- a/Content.Client/Options/UI/Tabs/MiscTab.xaml
+++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml
@@ -1,76 +1,34 @@
+ xmlns:tabs="clr-namespace:Content.Client.Options.UI.Tabs"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:ui="clr-namespace:Content.Client.Options.UI">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
index 13e3fd05f550ff..2aad4e1d0b6274 100644
--- a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
@@ -5,201 +5,54 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Player;
using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
-using Robust.Shared.Configuration;
-using Robust.Shared.Network;
-using Robust.Shared.Player;
using Robust.Shared.Prototypes;
-using Range = Robust.Client.UserInterface.Controls.Range;
-namespace Content.Client.Options.UI.Tabs
-{
- [GenerateTypedNameReferences]
- public sealed partial class MiscTab : Control
- {
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IConfigurationManager _cfg = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-
- private readonly Dictionary _hudThemeIdToIndex = new();
-
- public MiscTab()
- {
- RobustXamlLoader.Load(this);
- IoCManager.InjectDependencies(this);
-
- var themes = _prototypeManager.EnumeratePrototypes().ToList();
- themes.Sort();
- foreach (var gear in themes)
- {
- HudThemeOption.AddItem(Loc.GetString(gear.Name));
- _hudThemeIdToIndex.Add(gear.ID, HudThemeOption.GetItemId(HudThemeOption.ItemCount - 1));
- }
-
- var hudLayout = _cfg.GetCVar(CCVars.UILayout);
- var id = 0;
- foreach (var layout in Enum.GetValues(typeof(ScreenType)))
- {
- var name = layout.ToString()!;
- HudLayoutOption.AddItem(name, id);
- if (name == hudLayout)
- {
- HudLayoutOption.SelectId(id);
- }
- HudLayoutOption.SetItemMetadata(id, name);
-
- id++;
- }
-
- HudLayoutOption.OnItemSelected += args =>
- {
- HudLayoutOption.SelectId(args.Id);
- UpdateApplyButton();
- };
-
- // Channel can be null in replays so.
- // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
- ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel?.UserData.PatronTier is { };
-
- HudThemeOption.OnItemSelected += OnHudThemeChanged;
- DiscordRich.OnToggled += OnCheckBoxToggled;
- ShowOocPatronColor.OnToggled += OnCheckBoxToggled;
- ShowLoocAboveHeadCheckBox.OnToggled += OnCheckBoxToggled;
- ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled;
- ShowCombatModeIndicatorsCheckBox.OnToggled += OnCheckBoxToggled;
- OpaqueStorageWindowCheckBox.OnToggled += OnCheckBoxToggled;
- FancySpeechBubblesCheckBox.OnToggled += OnCheckBoxToggled;
- FancyNameBackgroundsCheckBox.OnToggled += OnCheckBoxToggled;
- EnableColorNameCheckBox.OnToggled += OnCheckBoxToggled;
- ColorblindFriendlyCheckBox.OnToggled += OnCheckBoxToggled;
- ReducedMotionCheckBox.OnToggled += OnCheckBoxToggled;
- ChatWindowOpacitySlider.OnValueChanged += OnChatWindowOpacitySliderChanged;
- ScreenShakeIntensitySlider.OnValueChanged += OnScreenShakeIntensitySliderChanged;
- // ToggleWalk.OnToggled += OnCheckBoxToggled;
- StaticStorageUI.OnToggled += OnCheckBoxToggled;
-
- HudThemeOption.SelectId(_hudThemeIdToIndex.GetValueOrDefault(_cfg.GetCVar(CVars.InterfaceTheme), 0));
- DiscordRich.Pressed = _cfg.GetCVar(CVars.DiscordEnabled);
- ShowOocPatronColor.Pressed = _cfg.GetCVar(CCVars.ShowOocPatronColor);
- ShowLoocAboveHeadCheckBox.Pressed = _cfg.GetCVar(CCVars.LoocAboveHeadShow);
- ShowHeldItemCheckBox.Pressed = _cfg.GetCVar(CCVars.HudHeldItemShow);
- ShowCombatModeIndicatorsCheckBox.Pressed = _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow);
- OpaqueStorageWindowCheckBox.Pressed = _cfg.GetCVar(CCVars.OpaqueStorageWindow);
- FancySpeechBubblesCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
- FancyNameBackgroundsCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatFancyNameBackground);
- EnableColorNameCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableColorName);
- ColorblindFriendlyCheckBox.Pressed = _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly);
- ReducedMotionCheckBox.Pressed = _cfg.GetCVar(CCVars.ReducedMotion);
- ChatWindowOpacitySlider.Value = _cfg.GetCVar(CCVars.ChatWindowOpacity);
- ScreenShakeIntensitySlider.Value = _cfg.GetCVar(CCVars.ScreenShakeIntensity) * 100f;
- // ToggleWalk.Pressed = _cfg.GetCVar(CCVars.ToggleWalk);
- StaticStorageUI.Pressed = _cfg.GetCVar(CCVars.StaticStorageUI);
-
-
- ApplyButton.OnPressed += OnApplyButtonPressed;
- UpdateApplyButton();
- }
+namespace Content.Client.Options.UI.Tabs;
- private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
- {
- UpdateApplyButton();
- }
+[GenerateTypedNameReferences]
+public sealed partial class MiscTab : Control
+{
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- private void OnHudThemeChanged(OptionButton.ItemSelectedEventArgs args)
- {
- HudThemeOption.SelectId(args.Id);
- UpdateApplyButton();
- }
+ public MiscTab()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
- private void OnChatWindowOpacitySliderChanged(Range range)
+ var themes = _prototypeManager.EnumeratePrototypes().ToList();
+ themes.Sort();
+ var themeEntries = new List.ValueOption>();
+ foreach (var gear in themes)
{
- ChatWindowOpacityLabel.Text = Loc.GetString("ui-options-chat-window-opacity-percent",
- ("opacity", range.Value));
- UpdateApplyButton();
+ themeEntries.Add(new OptionDropDownCVar.ValueOption(gear.ID, Loc.GetString(gear.Name)));
}
- private void OnScreenShakeIntensitySliderChanged(Range obj)
+ var layoutEntries = new List.ValueOption>();
+ foreach (var layout in Enum.GetValues(typeof(ScreenType)))
{
- ScreenShakeIntensityLabel.Text = Loc.GetString("ui-options-screen-shake-percent", ("intensity", ScreenShakeIntensitySlider.Value / 100f));
- UpdateApplyButton();
+ layoutEntries.Add(new OptionDropDownCVar.ValueOption(layout.ToString()!, layout.ToString()!));
}
- private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
- {
- foreach (var theme in _prototypeManager.EnumeratePrototypes())
- {
- if (_hudThemeIdToIndex[theme.ID] != HudThemeOption.SelectedId)
- continue;
- _cfg.SetCVar(CVars.InterfaceTheme, theme.ID);
- break;
- }
-
- _cfg.SetCVar(CVars.DiscordEnabled, DiscordRich.Pressed);
- _cfg.SetCVar(CCVars.HudHeldItemShow, ShowHeldItemCheckBox.Pressed);
- _cfg.SetCVar(CCVars.CombatModeIndicatorsPointShow, ShowCombatModeIndicatorsCheckBox.Pressed);
- _cfg.SetCVar(CCVars.OpaqueStorageWindow, OpaqueStorageWindowCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ShowOocPatronColor, ShowOocPatronColor.Pressed);
- _cfg.SetCVar(CCVars.LoocAboveHeadShow, ShowLoocAboveHeadCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ChatEnableColorName, EnableColorNameCheckBox.Pressed);
- _cfg.SetCVar(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ReducedMotion, ReducedMotionCheckBox.Pressed);
- _cfg.SetCVar(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider.Value);
- _cfg.SetCVar(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider.Value / 100f);
- // _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed);
- _cfg.SetCVar(CCVars.StaticStorageUI, StaticStorageUI.Pressed);
-
- if (HudLayoutOption.SelectedMetadata is string opt)
- {
- _cfg.SetCVar(CCVars.UILayout, opt);
- }
+ // Channel can be null in replays so.
+ // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
+ ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel?.UserData.PatronTier is { };
- _cfg.SaveToFile();
- UpdateApplyButton();
- }
+ Control.AddOptionDropDown(CVars.InterfaceTheme, DropDownHudTheme, themeEntries);
+ Control.AddOptionDropDown(CCVars.UILayout, DropDownHudLayout, layoutEntries);
- private void UpdateApplyButton()
- {
- var isHudThemeSame = HudThemeOption.SelectedId == _hudThemeIdToIndex.GetValueOrDefault(_cfg.GetCVar(CVars.InterfaceTheme), 0);
- var isLayoutSame = HudLayoutOption.SelectedMetadata is string opt && opt == _cfg.GetCVar(CCVars.UILayout);
- var isDiscordSame = DiscordRich.Pressed == _cfg.GetCVar(CVars.DiscordEnabled);
- var isShowHeldItemSame = ShowHeldItemCheckBox.Pressed == _cfg.GetCVar(CCVars.HudHeldItemShow);
- var isCombatModeIndicatorsSame = ShowCombatModeIndicatorsCheckBox.Pressed == _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow);
- var isOpaqueStorageWindow = OpaqueStorageWindowCheckBox.Pressed == _cfg.GetCVar(CCVars.OpaqueStorageWindow);
- var isOocPatronColorShowSame = ShowOocPatronColor.Pressed == _cfg.GetCVar(CCVars.ShowOocPatronColor);
- var isLoocShowSame = ShowLoocAboveHeadCheckBox.Pressed == _cfg.GetCVar(CCVars.LoocAboveHeadShow);
- var isFancyChatSame = FancySpeechBubblesCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
- var isFancyBackgroundSame = FancyNameBackgroundsCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatFancyNameBackground);
- var isEnableColorNameSame = EnableColorNameCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableColorName);
- var isColorblindFriendly = ColorblindFriendlyCheckBox.Pressed == _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly);
- var isReducedMotionSame = ReducedMotionCheckBox.Pressed == _cfg.GetCVar(CCVars.ReducedMotion);
- var isChatWindowOpacitySame = Math.Abs(ChatWindowOpacitySlider.Value - _cfg.GetCVar(CCVars.ChatWindowOpacity)) < 0.01f;
- var isScreenShakeIntensitySame = Math.Abs(ScreenShakeIntensitySlider.Value / 100f - _cfg.GetCVar(CCVars.ScreenShakeIntensity)) < 0.01f;
- // var isToggleWalkSame = ToggleWalk.Pressed == _cfg.GetCVar(CCVars.ToggleWalk);
- var isStaticStorageUISame = StaticStorageUI.Pressed == _cfg.GetCVar(CCVars.StaticStorageUI);
-
- ApplyButton.Disabled = isHudThemeSame &&
- isLayoutSame &&
- isDiscordSame &&
- isShowHeldItemSame &&
- isCombatModeIndicatorsSame &&
- isOpaqueStorageWindow &&
- isOocPatronColorShowSame &&
- isLoocShowSame &&
- isFancyChatSame &&
- isFancyBackgroundSame &&
- isEnableColorNameSame &&
- isColorblindFriendly &&
- isReducedMotionSame &&
- isChatWindowOpacitySame &&
- isScreenShakeIntensitySame &&
- // isToggleWalkSame &&
- isStaticStorageUISame;
- }
+ Control.AddOptionCheckBox(CVars.DiscordEnabled, DiscordRich);
+ Control.AddOptionCheckBox(CCVars.ShowOocPatronColor, ShowOocPatronColor);
+ Control.AddOptionCheckBox(CCVars.LoocAboveHeadShow, ShowLoocAboveHeadCheckBox);
+ Control.AddOptionCheckBox(CCVars.HudHeldItemShow, ShowHeldItemCheckBox);
+ Control.AddOptionCheckBox(CCVars.CombatModeIndicatorsPointShow, ShowCombatModeIndicatorsCheckBox);
+ Control.AddOptionCheckBox(CCVars.OpaqueStorageWindow, OpaqueStorageWindowCheckBox);
+ Control.AddOptionCheckBox(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox);
+ Control.AddOptionCheckBox(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox);
+ Control.AddOptionCheckBox(CCVars.StaticStorageUI, StaticStorageUI);
+ Control.Initialize();
}
-
}
diff --git a/Content.Client/Power/APC/ApcVisualizerComponent.cs b/Content.Client/Power/APC/ApcVisualizerComponent.cs
index e356a80177287e..87cb70019f5081 100644
--- a/Content.Client/Power/APC/ApcVisualizerComponent.cs
+++ b/Content.Client/Power/APC/ApcVisualizerComponent.cs
@@ -87,7 +87,7 @@ public sealed partial class ApcVisualsComponent : Component
///
[DataField("screenColors")]
[ViewVariables(VVAccess.ReadWrite)]
- public Color[] ScreenColors = new Color[(byte)ApcChargeState.NumStates]{Color.FromHex("#d1332e"), Color.FromHex("#dcdc28"), Color.FromHex("#82ff4c"), Color.FromHex("#ffac1c")};
+ public Color[] ScreenColors = new Color[(byte)ApcChargeState.NumStates]{Color.FromHex("#d1332e"), Color.FromHex("#2e8ad1"), Color.FromHex("#3db83b"), Color.FromHex("#ffac1c")};
///
/// The sprite state of the unlit overlay used for the APC screen when the APC has been emagged.
diff --git a/Content.Client/Rotation/RotationVisualizerSystem.cs b/Content.Client/Rotation/RotationVisualizerSystem.cs
index 6105c10c8033fe..6d3be4d1c05ded 100644
--- a/Content.Client/Rotation/RotationVisualizerSystem.cs
+++ b/Content.Client/Rotation/RotationVisualizerSystem.cs
@@ -23,8 +23,8 @@ private void OnAppearanceChange(EntityUid uid, RotationVisualsComponent componen
if (args.Sprite == null)
return;
- // If not defined, defaults to standing.
- _appearance.TryGetData(uid, RotationVisuals.RotationState, out var state, args.Component);
+ if (!_appearance.TryGetData(uid, RotationVisuals.RotationState, out var state, args.Component))
+ return;
switch (state)
{
diff --git a/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs b/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs
index 9fc132c7479259..307307c687a233 100644
--- a/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs
+++ b/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs
@@ -129,14 +129,7 @@ private void Update()
foreach (var stream in sensor.Streams.Values)
{
- var maxValue = stream.Unit switch
- {
- SensorUnit.PressureKpa => 5000, // 5 MPa
- SensorUnit.Ratio => 1,
- SensorUnit.PowerW => 1_000_000, // 1 MW
- SensorUnit.EnergyJ => 2_000_000, // 2 MJ
- _ => 1000
- };
+ var maxValue = stream.Samples.Max(x => x.Value);
// TODO: Better way to do this?
var lastSample = stream.Samples.Last();
@@ -151,7 +144,7 @@ private void Update()
}
});
- Asdf.AddChild(new GraphView(stream.Samples, startTime, curTime, maxValue) { MinHeight = 150 });
+ Asdf.AddChild(new GraphView(stream.Samples, startTime, curTime, maxValue * 1.1f) { MinHeight = 150 });
Asdf.AddChild(new PanelContainer { StyleClasses = { StyleBase.ClassLowDivider } });
}
}
diff --git a/Content.Client/Shuttles/FtlArrivalOverlay.cs b/Content.Client/Shuttles/FtlArrivalOverlay.cs
new file mode 100644
index 00000000000000..f24a1e964883cc
--- /dev/null
+++ b/Content.Client/Shuttles/FtlArrivalOverlay.cs
@@ -0,0 +1,82 @@
+using System.Numerics;
+using Content.Shared.Shuttles.Components;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Shuttles;
+
+///
+/// Plays a visualization whenever a shuttle is arriving from FTL.
+///
+public sealed class FtlArrivalOverlay : Overlay
+{
+ public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
+
+ private EntityLookupSystem _lookups;
+ private SharedMapSystem _maps;
+ private SharedTransformSystem _transforms;
+ private SpriteSystem _sprites;
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _protos = default!;
+
+ private readonly HashSet> _visualizers = new();
+
+ private ShaderInstance _shader;
+
+ public FtlArrivalOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _lookups = _entManager.System();
+ _transforms = _entManager.System();
+ _maps = _entManager.System();
+ _sprites = _entManager.System();
+
+ _shader = _protos.Index("unshaded").Instance();
+ }
+
+ protected override bool BeforeDraw(in OverlayDrawArgs args)
+ {
+ _visualizers.Clear();
+ _lookups.GetEntitiesOnMap(args.MapId, _visualizers);
+
+ return _visualizers.Count > 0;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ args.WorldHandle.UseShader(_shader);
+
+ foreach (var (uid, comp) in _visualizers)
+ {
+ var grid = comp.Grid;
+
+ if (!_entManager.TryGetComponent(grid, out MapGridComponent? mapGrid))
+ continue;
+
+ var texture = _sprites.GetFrame(comp.Sprite, TimeSpan.FromSeconds(comp.Elapsed), loop: false);
+ comp.Elapsed += (float) _timing.FrameTime.TotalSeconds;
+
+ // Need to manually transform the viewport in terms of the visualizer entity as the grid isn't in position.
+ var (_, _, worldMatrix, invMatrix) = _transforms.GetWorldPositionRotationMatrixWithInv(uid);
+ args.WorldHandle.SetTransform(worldMatrix);
+ var localAABB = invMatrix.TransformBox(args.WorldBounds);
+
+ var tilesEnumerator = _maps.GetLocalTilesEnumerator(grid, mapGrid, localAABB);
+
+ while (tilesEnumerator.MoveNext(out var tile))
+ {
+ var bounds = _lookups.GetLocalBounds(tile, mapGrid.TileSize);
+
+ args.WorldHandle.DrawTextureRect(texture, bounds);
+ }
+ }
+
+ args.WorldHandle.UseShader(null);
+ args.WorldHandle.SetTransform(Matrix3x2.Identity);
+ }
+}
diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
index d5154a87befeed..73c11de27952c1 100644
--- a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
+++ b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
@@ -38,9 +38,8 @@ public bool EnableShuttlePosition
private bool _enableShuttlePosition;
private EmergencyShuttleOverlay? _overlay;
- public override void Initialize()
+ private void InitializeEmergency()
{
- base.Initialize();
SubscribeNetworkEvent(OnShuttlePosMessage);
}
diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.cs
new file mode 100644
index 00000000000000..a2c048ff90ed8e
--- /dev/null
+++ b/Content.Client/Shuttles/Systems/ShuttleSystem.cs
@@ -0,0 +1,21 @@
+using Robust.Client.Graphics;
+
+namespace Content.Client.Shuttles.Systems;
+
+public sealed partial class ShuttleSystem
+{
+ [Dependency] private readonly IOverlayManager _overlays = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ InitializeEmergency();
+ _overlays.AddOverlay(new FtlArrivalOverlay());
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _overlays.RemoveOverlay();
+ }
+}
diff --git a/Content.Client/Starshine/Eye/NightVision/NightVisionOverlay.cs b/Content.Client/Starshine/Eye/NightVision/NightVisionOverlay.cs
index 7feb507c5e1c19..7442e95a7f667e 100644
--- a/Content.Client/Starshine/Eye/NightVision/NightVisionOverlay.cs
+++ b/Content.Client/Starshine/Eye/NightVision/NightVisionOverlay.cs
@@ -4,7 +4,7 @@
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
-namespace Content.Client.GG.Eye.NightVision
+namespace Content.Client.Starshine.Eye.NightVision
{
public sealed class NightVisionOverlay : Overlay
{
@@ -17,16 +17,16 @@ public sealed class NightVisionOverlay : Overlay
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _greyscaleShader;
- public Color NightvisionColor = Color.Green;
+ public Color Color;
- private NightVisionComponent _nightvisionComponent = default!;
+ private NightVisionComponent _nightVisionComponent = default!;
public NightVisionOverlay(Color color)
{
IoCManager.InjectDependencies(this);
_greyscaleShader = _prototypeManager.Index("GreyscaleFullscreen").InstanceUnique();
- NightvisionColor = color;
+ Color = color;
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
@@ -41,22 +41,20 @@ protected override bool BeforeDraw(in OverlayDrawArgs args)
if (playerEntity == null)
return false;
- if (!_entityManager.TryGetComponent(playerEntity, out var nightvisionComp))
+ if (!_entityManager.TryGetComponent(playerEntity, out var nightVisionComponent))
return false;
- _nightvisionComponent = nightvisionComp;
+ _nightVisionComponent = nightVisionComponent;
- var nightvision = _nightvisionComponent.IsNightVision;
+ var nightVision = _nightVisionComponent.IsOn;
- if (!nightvision && _nightvisionComponent.DrawShadows) // Disable our Night Vision
- {
- _lightManager.DrawLighting = true;
- _nightvisionComponent.DrawShadows = false;
- _nightvisionComponent.GraceFrame = true;
- return true;
- }
+ if (nightVision || !_nightVisionComponent.DrawShadows) // Disable our Night Vision
+ return nightVision;
+ _lightManager.DrawLighting = true;
+ _nightVisionComponent.DrawShadows = false;
+ _nightVisionComponent.GraceFrame = true;
+ return true;
- return nightvision;
}
protected override void Draw(in OverlayDrawArgs args)
@@ -64,22 +62,22 @@ protected override void Draw(in OverlayDrawArgs args)
if (ScreenTexture == null)
return;
- if (!_nightvisionComponent.GraceFrame)
+ if (!_nightVisionComponent.GraceFrame)
{
- _nightvisionComponent.DrawShadows = true; // Enable our Night Vision
+ _nightVisionComponent.DrawShadows = true; // Enable our Night Vision
_lightManager.DrawLighting = false;
}
else
{
- _nightvisionComponent.GraceFrame = false;
+ _nightVisionComponent.GraceFrame = false;
}
- _greyscaleShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+ _greyscaleShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.UseShader(_greyscaleShader);
- worldHandle.DrawRect(viewport, NightvisionColor);
+ worldHandle.DrawRect(viewport, Color);
worldHandle.UseShader(null);
}
}
diff --git a/Content.Client/Starshine/Eye/NightVision/NightVisionSystem.cs b/Content.Client/Starshine/Eye/NightVision/NightVisionSystem.cs
index 490db00dacf0b8..00494fb0be5c42 100644
--- a/Content.Client/Starshine/Eye/NightVision/NightVisionSystem.cs
+++ b/Content.Client/Starshine/Eye/NightVision/NightVisionSystem.cs
@@ -1,12 +1,9 @@
using Content.Client.Overlays;
-using Content.Shared.GameTicking;
using Content.Shared.Starshine.Eye.NightVision.Components;
using Content.Shared.Inventory.Events;
using Robust.Client.Graphics;
-using Robust.Client.Player;
-using Robust.Shared.Player;
-namespace Content.Client.GG.Eye.NightVision;
+namespace Content.Client.Starshine.Eye.NightVision;
public sealed class NightVisionSystem : EquipmentHudSystem
{
@@ -20,7 +17,7 @@ public override void Initialize()
{
base.Initialize();
- _overlay = new(Color.Green);
+ _overlay = new NightVisionOverlay(Color.Green);
}
protected override void UpdateInternal(RefreshEquipmentHudEvent component)
@@ -29,7 +26,7 @@ protected override void UpdateInternal(RefreshEquipmentHudEvent())
{
diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs
index b80a855f98655c..a74a118cd0f25a 100644
--- a/Content.Client/Storage/Systems/StorageSystem.cs
+++ b/Content.Client/Storage/Systems/StorageSystem.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using System.Numerics;
using Content.Client.Animations;
using Content.Shared.Hands;
@@ -69,7 +69,7 @@ public void OpenStorageWindow(Entity entity)
public void CloseStorageWindow(Entity entity)
{
- if (!Resolve(entity, ref entity.Comp))
+ if (!Resolve(entity, ref entity.Comp, false))
return;
if (!_openStorages.Contains((entity, entity.Comp)))
diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml b/Content.Client/Thief/ThiefBackpackMenu.xaml
index c1739eb321df3f..e46f18d4ed5504 100644
--- a/Content.Client/Thief/ThiefBackpackMenu.xaml
+++ b/Content.Client/Thief/ThiefBackpackMenu.xaml
@@ -5,7 +5,7 @@
MinSize="700 700">
-
+
diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs
index b2314cf3fe2d9c..543772c704ce4d 100644
--- a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs
+++ b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs
@@ -50,6 +50,7 @@ public void UpdateState(ThiefBackpackBoundUserInterfaceState state)
selectedNumber++;
}
+ Description.Text = Loc.GetString("thief-backpack-window-description", ("maxCount", state.MaxSelectedSets));
SelectedSets.Text = Loc.GetString("thief-backpack-window-selected", ("selectedCount", selectedNumber), ("maxCount", state.MaxSelectedSets));
ApproveButton.Disabled = selectedNumber == state.MaxSelectedSets ? false : true;
}
diff --git a/Content.Client/UserInterface/Controls/RadialContainer.cs b/Content.Client/UserInterface/Controls/RadialContainer.cs
index be263d12772e55..be9b8817a06514 100644
--- a/Content.Client/UserInterface/Controls/RadialContainer.cs
+++ b/Content.Client/UserInterface/Controls/RadialContainer.cs
@@ -67,11 +67,18 @@ public RadialContainer()
{
}
-
+
protected override void Draw(DrawingHandleScreen handle)
{
+
+ const float baseRadius = 100f;
+ const float radiusIncrement = 5f;
+
var children = ReserveSpaceForHiddenChildren ? Children : Children.Where(x => x.Visible);
var childCount = children.Count();
+
+ // Add padding from the center at higher child counts so they don't overlap.
+ Radius = baseRadius + (childCount * radiusIncrement);
// Determine the size of the arc, accounting for clockwise and anti-clockwise arrangements
var arc = AngularRange.Y - AngularRange.X;
diff --git a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
index e12ce0ab011c9b..d36a91c3733283 100644
--- a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
+++ b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
@@ -230,7 +230,7 @@ private void ObjectsTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? dat
if (function == EngineKeyFunctions.UIClick)
_conHost.ExecuteCommand($"vv {uid}");
- else if (function == EngineKeyFunctions.UseSecondary)
+ else if (function == EngineKeyFunctions.UIRightClick)
_verb.OpenVerbMenu(uid, true);
else
return;
diff --git a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs
index 6327757dec2dc1..19432cd799242d 100644
--- a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs
+++ b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs
@@ -52,20 +52,12 @@ public AlertControl(AlertPrototype alert, short? severity)
TooltipSupplier = SupplyTooltip;
Alert = alert;
_severity = severity;
-
- _spriteViewEntity = _entityManager.Spawn(Alert.AlertViewEntity);
- if (_entityManager.TryGetComponent(_spriteViewEntity, out var sprite))
- {
- var icon = Alert.GetIcon(_severity);
- if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer))
- sprite.LayerSetSprite(layer, icon);
- }
-
_icon = new SpriteView
{
Scale = new Vector2(2, 2)
};
- _icon.SetEntity(_spriteViewEntity);
+
+ SetupIcon();
Children.Add(_icon);
_cooldownGraphic = new CooldownGraphic
@@ -113,6 +105,36 @@ protected override void FrameUpdate(FrameEventArgs args)
_cooldownGraphic.FromTime(Cooldown.Value.Start, Cooldown.Value.End);
}
+ private void SetupIcon()
+ {
+ if (!_entityManager.Deleted(_spriteViewEntity))
+ _entityManager.QueueDeleteEntity(_spriteViewEntity);
+
+ _spriteViewEntity = _entityManager.Spawn(Alert.AlertViewEntity);
+ if (_entityManager.TryGetComponent(_spriteViewEntity, out var sprite))
+ {
+ var icon = Alert.GetIcon(_severity);
+ if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer))
+ sprite.LayerSetSprite(layer, icon);
+ }
+
+ _icon.SetEntity(_spriteViewEntity);
+ }
+
+ protected override void EnteredTree()
+ {
+ base.EnteredTree();
+ SetupIcon();
+ }
+
+ protected override void ExitedTree()
+ {
+ base.ExitedTree();
+
+ if (!_entityManager.Deleted(_spriteViewEntity))
+ _entityManager.QueueDeleteEntity(_spriteViewEntity);
+ }
+
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
diff --git a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
index 8b53290f7fb845..2c71fa8c40ef2a 100644
--- a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
+++ b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
@@ -4,10 +4,11 @@
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
-using Content.Client.Stylesheets;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
+using Content.Shared.IdentityManagement;
+using Robust.Shared.Timing;
namespace Content.Client.VendingMachines.UI
{
@@ -15,6 +16,10 @@ namespace Content.Client.VendingMachines.UI
public sealed partial class VendingMachineMenu : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ private readonly Dictionary _dummies = [];
public event Action? OnItemSelected;
public event Action? OnSearchChanged;
@@ -36,6 +41,22 @@ public VendingMachineMenu()
};
}
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ // Don't clean up dummies during disposal or we'll just have to spawn them again
+ if (!disposing)
+ return;
+
+ // Delete any dummy items we spawned
+ foreach (var entity in _dummies.Values)
+ {
+ _entityManager.QueueDeleteEntity(entity);
+ }
+ _dummies.Clear();
+ }
+
///
/// Populates the list of available items on the vending machine interface
/// and sets icons based on their prototypes
@@ -72,11 +93,16 @@ public void Populate(List inventory, out List
vendingItem.Text = string.Empty;
vendingItem.Icon = null;
- var itemName = entry.ID;
+ if (!_dummies.TryGetValue(entry.ID, out var dummy))
+ {
+ dummy = _entityManager.Spawn(entry.ID);
+ _dummies.Add(entry.ID, dummy);
+ }
+
+ var itemName = Identity.Name(dummy, _entityManager);
Texture? icon = null;
if (_prototypeManager.TryIndex(entry.ID, out var prototype))
{
- itemName = prototype.Name;
icon = spriteSystem.GetPrototypeIcon(prototype).Default;
}
diff --git a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
index 32b15252261857..c232e823132a0f 100644
--- a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
+++ b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
@@ -18,7 +18,7 @@ public sealed class ActionsAddedTest
[Test]
public async Task TestCombatActionsAdded()
{
- await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false});
+ await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false });
var server = pair.Server;
var client = pair.Client;
var sEntMan = server.ResolveDependency();
diff --git a/Content.IntegrationTests/Tests/Body/GibTest.cs b/Content.IntegrationTests/Tests/Body/GibTest.cs
index c0032a852445e3..4627c79f64deb8 100644
--- a/Content.IntegrationTests/Tests/Body/GibTest.cs
+++ b/Content.IntegrationTests/Tests/Body/GibTest.cs
@@ -5,7 +5,7 @@
namespace Content.IntegrationTests.Tests.Body;
[TestFixture]
-public sealed class GibTest
+public sealed class GibTest
{
[Test]
public async Task TestGib()
diff --git a/Content.IntegrationTests/Tests/Body/LegTest.cs b/Content.IntegrationTests/Tests/Body/LegTest.cs
index e86966f8f54e18..7b49bbe84a31ea 100644
--- a/Content.IntegrationTests/Tests/Body/LegTest.cs
+++ b/Content.IntegrationTests/Tests/Body/LegTest.cs
@@ -5,7 +5,6 @@
using Content.Shared.Rotation;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
-using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests.Body
{
@@ -40,13 +39,14 @@ public async Task RemoveLegsFallTest()
var appearanceSystem = entityManager.System();
var xformSystem = entityManager.System();
+ var map = await pair.CreateTestMap();
+
await server.WaitAssertion(() =>
{
- var mapId = mapManager.CreateMap();
BodyComponent body = null;
human = entityManager.SpawnEntity("HumanBodyAndAppearanceDummy",
- new MapCoordinates(Vector2.Zero, mapId));
+ new MapCoordinates(Vector2.Zero, map.MapId));
Assert.Multiple(() =>
{
@@ -61,7 +61,7 @@ await server.WaitAssertion(() =>
foreach (var leg in legs)
{
- xformSystem.DetachParentToNull(leg.Id, entityManager.GetComponent(leg.Id));
+ xformSystem.DetachEntity(leg.Id, entityManager.GetComponent(leg.Id));
}
});
diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs
index dce3741c98dcf7..9b5ee431f1fa9b 100644
--- a/Content.IntegrationTests/Tests/Body/LungTest.cs
+++ b/Content.IntegrationTests/Tests/Body/LungTest.cs
@@ -60,8 +60,8 @@ public async Task AirConsistencyTest()
var mapManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
var mapLoader = entityManager.System();
+ var mapSys = entityManager.System();
- MapId mapId;
EntityUid? grid = null;
BodyComponent body = default;
RespiratorComponent resp = default;
@@ -73,7 +73,7 @@ public async Task AirConsistencyTest()
await server.WaitPost(() =>
{
- mapId = mapManager.CreateMap();
+ mapSys.CreateMap(out var mapId);
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots));
var query = entityManager.GetEntityQuery();
@@ -142,8 +142,8 @@ public async Task NoSuffocationTest()
var entityManager = server.ResolveDependency();
var cfg = server.ResolveDependency();
var mapLoader = entityManager.System();
+ var mapSys = entityManager.System();
- MapId mapId;
EntityUid? grid = null;
RespiratorComponent respirator = null;
EntityUid human = default;
@@ -152,7 +152,7 @@ public async Task NoSuffocationTest()
await server.WaitPost(() =>
{
- mapId = mapManager.CreateMap();
+ mapSys.CreateMap(out var mapId);
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True);
var query = entityManager.GetEntityQuery();
diff --git a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
index 670ce1a474da4e..01482ba8ee217b 100644
--- a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
+++ b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
@@ -33,10 +33,11 @@ public async Task Test()
var mapLoader = entities.System();
var bodySystem = entities.System();
var containerSystem = entities.System();
+ var mapSys = entities.System();
await server.WaitAssertion(() =>
{
- var mapId = maps.CreateMap();
+ mapSys.CreateMap(out var mapId);
maps.CreateGrid(mapId);
var human = entities.SpawnEntity("HumanBodyDummy", new MapCoordinates(0, 0, mapId));
@@ -115,7 +116,7 @@ await server.WaitAssertion(() =>
mapLoader.SaveMap(mapId, mapPath);
maps.DeleteMap(mapId);
- mapId = maps.CreateMap();
+ mapSys.CreateMap(out mapId);
Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True);
var query = EnumerateQueryEnumerator(
diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs
new file mode 100644
index 00000000000000..82d5d3baa04c1c
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs
@@ -0,0 +1,56 @@
+using Content.IntegrationTests.Tests.Interaction;
+using Content.Shared.Buckle;
+using Content.Shared.Buckle.Components;
+using Content.Shared.Input;
+using Content.Shared.Movement.Pulling.Components;
+
+namespace Content.IntegrationTests.Tests.Buckle;
+
+public sealed class BuckleDragTest : InteractionTest
+{
+ // Check that dragging a buckled player unbuckles them.
+ [Test]
+ public async Task BucklePullTest()
+ {
+ var urist = await SpawnTarget("MobHuman");
+ var sUrist = ToServer(urist);
+ await SpawnTarget("Chair");
+
+ var buckle = Comp(urist);
+ var strap = Comp(Target);
+ var puller = Comp(Player);
+ var pullable = Comp(urist);
+
+#pragma warning disable RA0002
+ buckle.Delay = TimeSpan.Zero;
+#pragma warning restore RA0002
+
+ // Initially not buckled to the chair and not pulling anything
+ Assert.That(buckle.Buckled, Is.False);
+ Assert.That(buckle.BuckledTo, Is.Null);
+ Assert.That(strap.BuckledEntities, Is.Empty);
+ Assert.That(puller.Pulling, Is.Null);
+ Assert.That(pullable.Puller, Is.Null);
+ Assert.That(pullable.BeingPulled, Is.False);
+
+ // Strap the human to the chair
+ Assert.That(Server.System().TryBuckle(sUrist, SPlayer, STarget.Value));
+ await RunTicks(5);
+ Assert.That(buckle.Buckled, Is.True);
+ Assert.That(buckle.BuckledTo, Is.EqualTo(STarget));
+ Assert.That(strap.BuckledEntities, Is.EquivalentTo(new[] { sUrist }));
+ Assert.That(puller.Pulling, Is.Null);
+ Assert.That(pullable.Puller, Is.Null);
+ Assert.That(pullable.BeingPulled, Is.False);
+
+ // Start pulling, and thus unbuckle them
+ await PressKey(ContentKeyFunctions.TryPullObject, cursorEntity: urist);
+ await RunTicks(5);
+ Assert.That(buckle.Buckled, Is.False);
+ Assert.That(buckle.BuckledTo, Is.Null);
+ Assert.That(strap.BuckledEntities, Is.Empty);
+ Assert.That(puller.Pulling, Is.EqualTo(sUrist));
+ Assert.That(pullable.Puller, Is.EqualTo(SPlayer));
+ Assert.That(pullable.BeingPulled, Is.True);
+ }
+}
diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
index 57ac63b12476b3..156f42aac333c4 100644
--- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
+++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
@@ -91,7 +91,6 @@ await server.WaitAssertion(() =>
{
Assert.That(strap, Is.Not.Null);
Assert.That(strap.BuckledEntities, Is.Empty);
- Assert.That(strap.OccupiedSize, Is.Zero);
});
// Side effects of buckling
@@ -111,8 +110,6 @@ await server.WaitAssertion(() =>
// Side effects of buckling for the strap
Assert.That(strap.BuckledEntities, Does.Contain(human));
- Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
- Assert.That(strap.OccupiedSize, Is.Positive);
});
#pragma warning disable NUnit2045 // Interdependent asserts.
@@ -122,7 +119,7 @@ await server.WaitAssertion(() =>
// Trying to unbuckle too quickly fails
Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False);
Assert.That(buckle.Buckled);
- Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False);
+ Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False);
Assert.That(buckle.Buckled);
#pragma warning restore NUnit2045
});
@@ -149,7 +146,6 @@ await server.WaitAssertion(() =>
// Unbuckle, strap
Assert.That(strap.BuckledEntities, Is.Empty);
- Assert.That(strap.OccupiedSize, Is.Zero);
});
#pragma warning disable NUnit2045 // Interdependent asserts.
@@ -160,9 +156,9 @@ await server.WaitAssertion(() =>
// On cooldown
Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False);
Assert.That(buckle.Buckled);
- Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False);
+ Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False);
Assert.That(buckle.Buckled);
- Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False);
+ Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False);
Assert.That(buckle.Buckled);
#pragma warning restore NUnit2045
});
@@ -189,7 +185,6 @@ await server.WaitAssertion(() =>
#pragma warning disable NUnit2045 // Interdependent asserts.
Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle), Is.False);
Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False);
- Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False);
#pragma warning restore NUnit2045
// Move near the chair
@@ -202,12 +197,10 @@ await server.WaitAssertion(() =>
Assert.That(buckle.Buckled);
Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False);
Assert.That(buckle.Buckled);
- Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False);
- Assert.That(buckle.Buckled);
#pragma warning restore NUnit2045
// Force unbuckle
- Assert.That(buckleSystem.TryUnbuckle(human, human, true, buckleComp: buckle));
+ buckleSystem.Unbuckle(human, human);
Assert.Multiple(() =>
{
Assert.That(buckle.Buckled, Is.False);
@@ -311,7 +304,7 @@ await server.WaitAssertion(() =>
// Break our guy's kneecaps
foreach (var leg in legs)
{
- xformSystem.DetachParentToNull(leg.Id, entityManager.GetComponent(leg.Id));
+ entityManager.DeleteEntity(leg.Id);
}
});
@@ -328,7 +321,8 @@ await server.WaitAssertion(() =>
Assert.That(hand.HeldEntity, Is.Null);
}
- buckleSystem.TryUnbuckle(human, human, true, buckleComp: buckle);
+ buckleSystem.Unbuckle(human, human);
+ Assert.That(buckle.Buckled, Is.False);
});
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs
index 8e1d536054f2b9..89018d9d17c66c 100644
--- a/Content.IntegrationTests/Tests/CargoTest.cs
+++ b/Content.IntegrationTests/Tests/CargoTest.cs
@@ -19,11 +19,11 @@ namespace Content.IntegrationTests.Tests;
[TestFixture]
public sealed class CargoTest
{
- public static HashSet> Ignored = new ()
- {
+ private static readonly HashSet> Ignored =
+ [
// This is ignored because it is explicitly intended to be able to sell for more than it costs.
new("FunCrateGambling")
- };
+ ];
[Test]
public async Task NoCargoOrderArbitrage()
diff --git a/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs b/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs
index 8e3b89bff11b43..0e3f89c2825d29 100644
--- a/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs
+++ b/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs
@@ -9,10 +9,10 @@ namespace Content.IntegrationTests.Tests.Chemistry
{
public sealed class FixedPoint2SerializationTest : SerializationTest
{
- protected override Assembly[] Assemblies => new[]
- {
+ protected override Assembly[] Assemblies =>
+ [
typeof(FixedPoint2SerializationTest).Assembly
- };
+ ];
[Test]
public void DeserializeNullTest()
@@ -53,6 +53,6 @@ public void DeserializeNullDefinitionTest()
[DataDefinition]
public sealed partial class FixedPoint2TestDefinition
{
- [DataField("unit")] public FixedPoint2? Unit { get; set; } = FixedPoint2.New(5);
+ [DataField] public FixedPoint2? Unit { get; set; } = FixedPoint2.New(5);
}
}
diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs
index 4d19a96d9e7eae..89d33186a2735d 100644
--- a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs
+++ b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs
@@ -1,5 +1,5 @@
-using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
@@ -76,7 +76,7 @@ public async Task Test()
await server.WaitPost(() =>
{
- var system = server.System();
+ var system = server.System();
var beaker = server.EntMan.SpawnEntity("SolutionRoundingTestContainer", testMap.GridCoords);
system.TryGetSolution(beaker, "beaker", out var newSolutionEnt, out var newSolution);
diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs
index d96a035b2dc65a..6b71dd08be0018 100644
--- a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs
+++ b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs
@@ -1,5 +1,5 @@
-using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
@@ -11,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Chemistry;
// To ensure volume(A) + volume(B) = volume(A+B)
// reactions can change this assumption
[TestFixture]
-[TestOf(typeof(SolutionContainerSystem))]
+[TestOf(typeof(SharedSolutionContainerSystem))]
public sealed class SolutionSystemTests
{
[TestPrototypes]
@@ -51,7 +51,7 @@ public async Task TryAddTwoNonReactiveReagent()
var entityManager = server.ResolveDependency();
var protoMan = server.ResolveDependency();
- var containerSystem = entityManager.System();
+ var containerSystem = entityManager.System();
var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords;
@@ -97,7 +97,7 @@ public async Task TryAddTooMuchNonReactiveReagent()
var entityManager = server.ResolveDependency();
var protoMan = server.ResolveDependency();
- var containerSystem = entityManager.System();
+ var containerSystem = entityManager.System();
var coordinates = testMap.GridCoords;
EntityUid beaker;
@@ -141,7 +141,7 @@ public async Task TryMixAndOverflowTooMuchReagent()
var entityManager = server.ResolveDependency();
var protoMan = server.ResolveDependency();
var testMap = await pair.CreateTestMap();
- var containerSystem = entityManager.System();
+ var containerSystem = entityManager.System();
var coordinates = testMap.GridCoords;
EntityUid beaker;
@@ -194,7 +194,7 @@ public async Task TryMixAndOverflowTooBigOverflow()
var entityManager = server.ResolveDependency();
var protoMan = server.ResolveDependency();
- var containerSystem = entityManager.System();
+ var containerSystem = entityManager.System();
var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords;
diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs
index ddfe7b3481e374..3664cda922a527 100644
--- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs
+++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs
@@ -1,4 +1,3 @@
-using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Components;
using Robust.Shared.GameObjects;
@@ -6,6 +5,7 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using System.Linq;
+using Content.Shared.Chemistry.EntitySystems;
namespace Content.IntegrationTests.Tests.Chemistry
{
@@ -34,7 +34,7 @@ public async Task TryAllTest()
var prototypeManager = server.ResolveDependency();
var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords;
- var solutionContainerSystem = entityManager.System();
+ var solutionContainerSystem = entityManager.System();
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes())
{
diff --git a/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs b/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs
index d8d3086520eba8..2db0a9acd3d7b5 100644
--- a/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs
+++ b/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs
@@ -1,5 +1,6 @@
#nullable enable
using Content.IntegrationTests.Tests.Interaction;
+using Content.IntegrationTests.Tests.Movement;
using Robust.Shared.Maths;
using ClimbingComponent = Content.Shared.Climbing.Components.ClimbingComponent;
using ClimbSystem = Content.Shared.Climbing.Systems.ClimbSystem;
diff --git a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs
new file mode 100644
index 00000000000000..3fa7e64f1a4b3b
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs
@@ -0,0 +1,88 @@
+using Content.Server.Maps;
+using Content.Shared.CCVar;
+using Robust.Shared.Configuration;
+using Robust.Shared.Console;
+
+namespace Content.IntegrationTests.Tests.Commands;
+
+[TestFixture]
+public sealed class ForceMapTest
+{
+ private const string DefaultMapName = "Empty";
+ private const string BadMapName = "asdf_asd-fa__sdfAsd_f"; // Hopefully no one ever names a map this...
+ private const string TestMapEligibleName = "ForceMapTestEligible";
+ private const string TestMapIneligibleName = "ForceMapTestIneligible";
+
+ [TestPrototypes]
+ private static readonly string TestMaps = @$"
+- type: gameMap
+ id: {TestMapIneligibleName}
+ mapName: {TestMapIneligibleName}
+ mapPath: /Maps/Test/empty.yml
+ minPlayers: 20
+ maxPlayers: 80
+ stations:
+ Empty:
+ stationProto: StandardNanotrasenStation
+ components:
+ - type: StationNameSetup
+ mapNameTemplate: ""Empty""
+
+- type: gameMap
+ id: {TestMapEligibleName}
+ mapName: {TestMapEligibleName}
+ mapPath: /Maps/Test/empty.yml
+ minPlayers: 0
+ stations:
+ Empty:
+ stationProto: StandardNanotrasenStation
+ components:
+ - type: StationNameSetup
+ mapNameTemplate: ""Empty""
+";
+
+ [Test]
+ public async Task TestForceMapCommand()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var server = pair.Server;
+
+ var entMan = server.EntMan;
+ var configManager = server.ResolveDependency();
+ var consoleHost = server.ResolveDependency();
+ var gameMapMan = server.ResolveDependency();
+
+ await server.WaitAssertion(() =>
+ {
+ // Make sure we're set to the default map
+ Assert.That(gameMapMan.GetSelectedMap()?.ID, Is.EqualTo(DefaultMapName),
+ $"Test didn't start on expected map ({DefaultMapName})!");
+
+ // Try changing to a map that doesn't exist
+ consoleHost.ExecuteCommand($"forcemap {BadMapName}");
+ Assert.That(gameMapMan.GetSelectedMap()?.ID, Is.EqualTo(DefaultMapName),
+ $"Forcemap succeeded with a map that does not exist ({BadMapName})!");
+
+ // Try changing to a valid map
+ consoleHost.ExecuteCommand($"forcemap {TestMapEligibleName}");
+ Assert.That(gameMapMan.GetSelectedMap()?.ID, Is.EqualTo(TestMapEligibleName),
+ $"Forcemap failed with a valid map ({TestMapEligibleName})");
+
+ // Try changing to a map that exists but is ineligible
+ consoleHost.ExecuteCommand($"forcemap {TestMapIneligibleName}");
+ Assert.That(gameMapMan.GetSelectedMap()?.ID, Is.EqualTo(TestMapIneligibleName),
+ $"Forcemap failed with valid but ineligible map ({TestMapIneligibleName})!");
+
+ // Try clearing the force-selected map
+ consoleHost.ExecuteCommand("forcemap \"\"");
+ Assert.That(gameMapMan.GetSelectedMap(), Is.Null,
+ $"Running 'forcemap \"\"' did not clear the forced map!");
+
+ });
+
+ // Cleanup
+ configManager.SetCVar(CCVars.GameMap, DefaultMapName);
+
+ await pair.CleanReturnAsync();
+ }
+}
diff --git a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs
index b3a66e3211cc84..4db9eabf5c6b85 100644
--- a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs
+++ b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs
@@ -28,7 +28,7 @@ public async Task PardonTest()
Assert.That(netMan.IsConnected);
- Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1));
+ Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1));
// No bans on record
Assert.Multiple(async () =>
{
@@ -50,7 +50,7 @@ public async Task PardonTest()
var banReason = "test";
- Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1));
+ Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1));
// Ban the client for 24 hours
await server.WaitPost(() => sConsole.ExecuteCommand($"ban {clientSession.Name} {banReason} 1440"));
@@ -63,7 +63,7 @@ public async Task PardonTest()
});
await pair.RunTicksSync(5);
- Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(0));
+ Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(0));
Assert.That(!netMan.IsConnected);
// Try to pardon a ban that does not exist
@@ -143,11 +143,11 @@ public async Task PardonTest()
});
// Reconnect client. Slightly faster than dirtying the pair.
- Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(0));
+ Assert.That(sPlayerManager.Sessions, Is.Empty);
client.SetConnectTarget(server);
await client.WaitPost(() => netMan.ClientConnect(null!, 0, null!));
await pair.RunTicksSync(5);
- Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1));
+ Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1));
await pair.CleanReturnAsync();
}
diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
index 2fda3ad58e6bef..cfc80073066979 100644
--- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
+++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
@@ -37,9 +37,9 @@ public async Task RejuvenateDeadTest()
var server = pair.Server;
var entManager = server.ResolveDependency();
var prototypeManager = server.ResolveDependency();
- var mobStateSystem = entManager.EntitySysManager.GetEntitySystem();
- var damSystem = entManager.EntitySysManager.GetEntitySystem();
- var rejuvenateSystem = entManager.EntitySysManager.GetEntitySystem();
+ var mobStateSystem = entManager.System();
+ var damSystem = entManager.System();
+ var rejuvenateSystem = entManager.System();
await server.WaitAssertion(() =>
{
diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs
index ff24ec09686130..72a05b5246f7bc 100644
--- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs
+++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs
@@ -26,7 +26,7 @@ public async Task RestartRoundAfterStart(bool lobbyEnabled)
var configManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
- var gameTicker = entityManager.EntitySysManager.GetEntitySystem();
+ var gameTicker = entityManager.System();
await pair.RunTicksSync(5);
diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs b/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs
index 76911eba5f709d..74d0e9242176e3 100644
--- a/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs
+++ b/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs
@@ -59,11 +59,6 @@ public async Task CraftSpear()
await AssertEntityLookup((Rod, 2), (Cable, 7), (ShardGlass, 2), (Spear, 1));
}
- // The following is wrapped in an if DEBUG. This is because of cursed state handling bugs. Tests don't (de)serialize
- // net messages and just copy objects by reference. This means that the server will directly modify cached server
- // states on the client's end. Crude fix at the moment is to used modified state handling while in debug mode
- // Otherwise, this test cannot work.
-#if DEBUG
///
/// Cancel crafting a complex recipe.
///
@@ -93,28 +88,22 @@ public async Task CancelCraft()
await RunTicks(1);
// DoAfter is in progress. Entity not spawned, stacks have been split and someingredients are in a container.
- Assert.Multiple(async () =>
- {
- Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
- Assert.That(sys.IsEntityInContainer(shard), Is.True);
- Assert.That(sys.IsEntityInContainer(rods), Is.False);
- Assert.That(sys.IsEntityInContainer(wires), Is.False);
- Assert.That(rodStack, Has.Count.EqualTo(8));
- Assert.That(wireStack, Has.Count.EqualTo(7));
+ Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
+ Assert.That(sys.IsEntityInContainer(shard), Is.True);
+ Assert.That(sys.IsEntityInContainer(rods), Is.False);
+ Assert.That(sys.IsEntityInContainer(wires), Is.False);
+ Assert.That(rodStack, Has.Count.EqualTo(8));
+ Assert.That(wireStack, Has.Count.EqualTo(7));
- await FindEntity(Spear, shouldSucceed: false);
- });
+ await FindEntity(Spear, shouldSucceed: false);
// Cancel the DoAfter. Should drop ingredients to the floor.
await CancelDoAfters();
- Assert.Multiple(async () =>
- {
- Assert.That(sys.IsEntityInContainer(rods), Is.False);
- Assert.That(sys.IsEntityInContainer(wires), Is.False);
- Assert.That(sys.IsEntityInContainer(shard), Is.False);
- await FindEntity(Spear, shouldSucceed: false);
- await AssertEntityLookup((Rod, 10), (Cable, 10), (ShardGlass, 1));
- });
+ Assert.That(sys.IsEntityInContainer(rods), Is.False);
+ Assert.That(sys.IsEntityInContainer(wires), Is.False);
+ Assert.That(sys.IsEntityInContainer(shard), Is.False);
+ await FindEntity(Spear, shouldSucceed: false);
+ await AssertEntityLookup((Rod, 10), (Cable, 10), (ShardGlass, 1));
// Re-attempt the do-after
#pragma warning disable CS4014 // Legacy construction code uses DoAfterAwait. See above.
@@ -123,24 +112,17 @@ public async Task CancelCraft()
await RunTicks(1);
// DoAfter is in progress. Entity not spawned, ingredients are in a container.
- Assert.Multiple(async () =>
- {
- Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
- Assert.That(sys.IsEntityInContainer(shard), Is.True);
- await FindEntity(Spear, shouldSucceed: false);
- });
+ Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
+ Assert.That(sys.IsEntityInContainer(shard), Is.True);
+ await FindEntity(Spear, shouldSucceed: false);
// Finish the DoAfter
await AwaitDoAfters();
// Spear has been crafted. Rods and wires are no longer contained. Glass has been consumed.
- Assert.Multiple(async () =>
- {
- await FindEntity(Spear);
- Assert.That(sys.IsEntityInContainer(rods), Is.False);
- Assert.That(sys.IsEntityInContainer(wires), Is.False);
- Assert.That(SEntMan.Deleted(shard));
- });
+ await FindEntity(Spear);
+ Assert.That(sys.IsEntityInContainer(rods), Is.False);
+ Assert.That(sys.IsEntityInContainer(wires), Is.False);
+ Assert.That(SEntMan.Deleted(shard));
}
-#endif
}
diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs
index c61a70faf0b5bf..37c4b0c9b5755e 100644
--- a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs
+++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs
@@ -43,11 +43,11 @@ public async Task TestA()
EntityUid dummy = default;
var mapManager = server.ResolveDependency();
- var mapId = mapManager.CreateMap();
+ var map = await pair.CreateTestMap();
await server.WaitPost(() =>
{
- var pos = new MapCoordinates(Vector2.Zero, mapId);
+ var pos = new MapCoordinates(Vector2.Zero, map.MapId);
var entStorage = serverEntManager.EntitySysManager.GetEntitySystem();
var container = serverEntManager.SpawnEntity("ContainerOcclusionA", pos);
dummy = serverEntManager.SpawnEntity("ContainerOcclusionDummy", pos);
@@ -85,11 +85,12 @@ public async Task TestB()
EntityUid dummy = default;
var mapManager = server.ResolveDependency();
- var mapId = mapManager.CreateMap();
+
+ var map = await pair.CreateTestMap();
await server.WaitPost(() =>
{
- var pos = new MapCoordinates(Vector2.Zero, mapId);
+ var pos = new MapCoordinates(Vector2.Zero, map.MapId);
var entStorage = serverEntManager.EntitySysManager.GetEntitySystem();
var container = serverEntManager.SpawnEntity("ContainerOcclusionB", pos);
dummy = serverEntManager.SpawnEntity("ContainerOcclusionDummy", pos);
@@ -127,11 +128,12 @@ public async Task TestAb()
EntityUid dummy = default;
var mapManager = server.ResolveDependency();
- var mapId = mapManager.CreateMap();
+
+ var map = await pair.CreateTestMap();
await server.WaitPost(() =>
{
- var pos = new MapCoordinates(Vector2.Zero, mapId);
+ var pos = new MapCoordinates(Vector2.Zero, map.MapId);
var entStorage = serverEntManager.EntitySysManager.GetEntitySystem();
var containerA = serverEntManager.SpawnEntity("ContainerOcclusionA", pos);
var containerB = serverEntManager.SpawnEntity("ContainerOcclusionB", pos);
diff --git a/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs
index 41d17ddedae4c5..bd5cac05dd14d2 100644
--- a/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs
+++ b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs
@@ -14,39 +14,39 @@ public void TestDamageSpecifierOperations()
// Test basic math operations.
// I've already nearly broken these once. When editing the operators.
- DamageSpecifier input1 = new() { DamageDict = _input1 };
- DamageSpecifier input2 = new() { DamageDict = _input2 };
- DamageSpecifier output1 = new() { DamageDict = _output1 };
- DamageSpecifier output2 = new() { DamageDict = _output2 };
- DamageSpecifier output3 = new() { DamageDict = _output3 };
- DamageSpecifier output4 = new() { DamageDict = _output4 };
- DamageSpecifier output5 = new() { DamageDict = _output5 };
+ DamageSpecifier input1 = new() { DamageDict = Input1 };
+ DamageSpecifier input2 = new() { DamageDict = Input2 };
+ DamageSpecifier output1 = new() { DamageDict = Output1 };
+ DamageSpecifier output2 = new() { DamageDict = Output2 };
+ DamageSpecifier output3 = new() { DamageDict = Output3 };
+ DamageSpecifier output4 = new() { DamageDict = Output4 };
+ DamageSpecifier output5 = new() { DamageDict = Output5 };
Assert.Multiple(() =>
{
- Assert.That((-input1).Equals(output1));
- Assert.That((input1 / 2).Equals(output2));
- Assert.That((input1 * 2).Equals(output3));
+ Assert.That(-input1, Is.EqualTo(output1));
+ Assert.That(input1 / 2, Is.EqualTo(output2));
+ Assert.That(input1 * 2, Is.EqualTo(output3));
});
- var difference = (input1 - input2);
- Assert.That(difference.Equals(output4));
+ var difference = input1 - input2;
+ Assert.That(difference, Is.EqualTo(output4));
- var difference2 = (-input2) + input1;
- Assert.That(difference.Equals(difference2));
+ var difference2 = -input2 + input1;
+ Assert.That(difference, Is.EqualTo(difference2));
difference.Clamp(-0.25f, 0.25f);
- Assert.That(difference.Equals(output5));
+ Assert.That(difference, Is.EqualTo(output5));
}
- static Dictionary _input1 = new()
+ private static readonly Dictionary Input1 = new()
{
{ "A", 1.5f },
{ "B", 2 },
{ "C", 3 }
};
- static Dictionary _input2 = new()
+ private static readonly Dictionary Input2 = new()
{
{ "A", 1 },
{ "B", 2 },
@@ -54,28 +54,28 @@ public void TestDamageSpecifierOperations()
{ "D", 0.05f }
};
- static Dictionary _output1 = new()
+ private static readonly Dictionary Output1 = new()
{
{ "A", -1.5f },
{ "B", -2 },
{ "C", -3 }
};
- static Dictionary _output2 = new()
+ private static readonly Dictionary Output2 = new()
{
{ "A", 0.75f },
{ "B", 1 },
{ "C", 1.5 }
};
- static Dictionary _output3 = new()
+ private static readonly Dictionary Output3 = new()
{
{ "A", 3f },
{ "B", 4 },
{ "C", 6 }
};
- static Dictionary _output4 = new()
+ private static readonly Dictionary Output4 = new()
{
{ "A", 0.5f },
{ "B", 0 },
@@ -83,7 +83,7 @@ public void TestDamageSpecifierOperations()
{ "D", -0.05f }
};
- static Dictionary _output5 = new()
+ private static readonly Dictionary Output5 = new()
{
{ "A", 0.25f },
{ "B", 0 },
diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs
index c40b8ed286f666..69069fc82fee28 100644
--- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs
+++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs
@@ -107,10 +107,11 @@ public async Task TestDamageableComponents()
FixedPoint2 typeDamage;
+ var map = await pair.CreateTestMap();
+
await server.WaitPost(() =>
{
- var map = sMapManager.CreateMap();
- var coordinates = new MapCoordinates(0, 0, map);
+ var coordinates = map.MapCoords;
sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates);
sDamageableComponent = sEntityManager.GetComponent(sDamageableEntity);
diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs
index 2fbaa91456f595..e47c73611a4145 100644
--- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs
+++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs
@@ -123,24 +123,24 @@ public async Task AirlockBlockTest()
var xformSystem = entityManager.System();
PhysicsComponent physBody = null;
- EntityUid AirlockPhysicsDummy = default;
+ EntityUid airlockPhysicsDummy = default;
EntityUid airlock = default;
DoorComponent doorComponent = null;
- var AirlockPhysicsDummyStartingX = -1;
+ var airlockPhysicsDummyStartingX = -1;
+
+ var map = await pair.CreateTestMap();
await server.WaitAssertion(() =>
{
- var mapId = mapManager.CreateMap();
-
- var humanCoordinates = new MapCoordinates(new Vector2(AirlockPhysicsDummyStartingX, 0), mapId);
- AirlockPhysicsDummy = entityManager.SpawnEntity("AirlockPhysicsDummy", humanCoordinates);
+ var humanCoordinates = new MapCoordinates(new Vector2(airlockPhysicsDummyStartingX, 0), map.MapId);
+ airlockPhysicsDummy = entityManager.SpawnEntity("AirlockPhysicsDummy", humanCoordinates);
- airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates(new Vector2(0, 0), mapId));
+ airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates(new Vector2(0, 0), map.MapId));
Assert.Multiple(() =>
{
- Assert.That(entityManager.TryGetComponent(AirlockPhysicsDummy, out physBody), Is.True);
+ Assert.That(entityManager.TryGetComponent(airlockPhysicsDummy, out physBody), Is.True);
Assert.That(entityManager.TryGetComponent(airlock, out doorComponent), Is.True);
});
Assert.That(doorComponent.State, Is.EqualTo(DoorState.Closed));
@@ -152,7 +152,7 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() => Assert.That(physBody, Is.Not.EqualTo(null)));
await server.WaitPost(() =>
{
- physicsSystem.SetLinearVelocity(AirlockPhysicsDummy, new Vector2(0.5f, 0f), body: physBody);
+ physicsSystem.SetLinearVelocity(airlockPhysicsDummy, new Vector2(0.5f, 0f), body: physBody);
});
for (var i = 0; i < 240; i += 10)
@@ -176,7 +176,7 @@ await server.WaitPost(() =>
// Blocked by the airlock
await server.WaitAssertion(() =>
{
- Assert.That(Math.Abs(xformSystem.GetWorldPosition(AirlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f));
+ Assert.That(Math.Abs(xformSystem.GetWorldPosition(airlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f));
});
await pair.CleanReturnAsync();
}
diff --git a/Content.IntegrationTests/Tests/DummyIconTest.cs b/Content.IntegrationTests/Tests/DummyIconTest.cs
index a11191a51ea349..df2d28a2ea27dd 100644
--- a/Content.IntegrationTests/Tests/DummyIconTest.cs
+++ b/Content.IntegrationTests/Tests/DummyIconTest.cs
@@ -21,7 +21,7 @@ await client.WaitAssertion(() =>
{
foreach (var proto in prototypeManager.EnumeratePrototypes())
{
- if (proto.NoSpawn || proto.Abstract || pair.IsTestPrototype(proto) || !proto.Components.ContainsKey("Sprite"))
+ if (proto.HideSpawnMenu || proto.Abstract || pair.IsTestPrototype(proto) || !proto.Components.ContainsKey("Sprite"))
continue;
Assert.DoesNotThrow(() =>
diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs
index 1fc739fb0c70bc..42bea8989cdf4b 100644
--- a/Content.IntegrationTests/Tests/EntityTest.cs
+++ b/Content.IntegrationTests/Tests/EntityTest.cs
@@ -1,15 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
-using Content.Server.Humanoid.Components;
-using Content.Shared.Coordinates;
-using Content.Shared.Prototypes;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
@@ -47,7 +43,7 @@ await server.WaitPost(() =>
foreach (var protoId in protoIds)
{
- var mapId = mapManager.CreateMap();
+ mapSystem.CreateMap(out var mapId);
var grid = mapManager.CreateGridEntity(mapId);
// TODO: Fix this better in engine.
mapSystem.SetTile(grid.Owner, grid.Comp, Vector2i.Zero, new Tile(1));
@@ -155,6 +151,7 @@ public async Task SpawnAndDirtyAllEntities()
var prototypeMan = server.ResolveDependency();
var mapManager = server.ResolveDependency();
var sEntMan = server.ResolveDependency();
+ var mapSys = server.System();
Assert.That(cfg.GetCVar(CVars.NetPVS), Is.False);
@@ -170,7 +167,7 @@ await server.WaitPost(() =>
{
foreach (var protoId in protoIds)
{
- var mapId = mapManager.CreateMap();
+ mapSys.CreateMap(out var mapId);
var grid = mapManager.CreateGridEntity(mapId);
var ent = sEntMan.SpawnEntity(protoId, new EntityCoordinates(grid.Owner, 0.5f, 0.5f));
foreach (var (_, component) in sEntMan.GetNetComponents(ent))
@@ -227,6 +224,7 @@ public async Task SpawnAndDeleteEntityCountTest()
var settings = new PoolSettings { Connected = true, Dirty = true };
await using var pair = await PoolManager.GetServerClient(settings);
var mapManager = pair.Server.ResolveDependency();
+ var mapSys = pair.Server.System();
var server = pair.Server;
var client = pair.Client;
@@ -256,7 +254,7 @@ public async Task SpawnAndDeleteEntityCountTest()
await server.WaitPost(() =>
{
- mapId = mapManager.CreateMap();
+ mapSys.CreateMap(out mapId);
});
var coords = new MapCoordinates(Vector2.Zero, mapId);
diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
index 6e88d6928e6a9f..d6f9bf359860dd 100644
--- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
+++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
@@ -16,14 +16,15 @@ namespace Content.IntegrationTests.Tests.Fluids;
[TestOf(typeof(SpreaderSystem))]
public sealed class FluidSpill
{
- private static PuddleComponent? GetPuddle(IEntityManager entityManager, MapGridComponent mapGrid, Vector2i pos)
+ private static PuddleComponent? GetPuddle(IEntityManager entityManager, Entity mapGrid, Vector2i pos)
{
return GetPuddleEntity(entityManager, mapGrid, pos)?.Comp;
}
- private static Entity? GetPuddleEntity(IEntityManager entityManager, MapGridComponent mapGrid, Vector2i pos)
+ private static Entity? GetPuddleEntity(IEntityManager entityManager, Entity mapGrid, Vector2i pos)
{
- foreach (var uid in mapGrid.GetAnchoredEntities(pos))
+ var mapSys = entityManager.System();
+ foreach (var uid in mapSys.GetAnchoredEntities(mapGrid, mapGrid.Comp, pos))
{
if (entityManager.TryGetComponent(uid, out PuddleComponent? puddleComponent))
return (uid, puddleComponent);
@@ -39,9 +40,9 @@ public async Task SpillCorner()
var server = pair.Server;
var mapManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
- var puddleSystem = server.ResolveDependency().GetEntitySystem();
+ var puddleSystem = server.System();
+ var mapSystem = server.System();
var gameTiming = server.ResolveDependency();
- MapId mapId;
EntityUid gridId = default;
/*
@@ -52,7 +53,7 @@ . . .
*/
await server.WaitPost(() =>
{
- mapId = mapManager.CreateMap();
+ mapSystem.CreateMap(out var mapId);
var grid = mapManager.CreateGridEntity(mapId);
gridId = grid.Owner;
@@ -60,12 +61,12 @@ await server.WaitPost(() =>
{
for (var y = 0; y < 3; y++)
{
- grid.Comp.SetTile(new Vector2i(x, y), new Tile(1));
+ mapSystem.SetTile(grid, new Vector2i(x, y), new Tile(1));
}
}
- entityManager.SpawnEntity("WallReinforced", grid.Comp.GridTileToLocal(new Vector2i(0, 1)));
- entityManager.SpawnEntity("WallReinforced", grid.Comp.GridTileToLocal(new Vector2i(1, 0)));
+ entityManager.SpawnEntity("WallReinforced", mapSystem.GridTileToLocal(grid, grid.Comp, new Vector2i(0, 1)));
+ entityManager.SpawnEntity("WallReinforced", mapSystem.GridTileToLocal(grid, grid.Comp, new Vector2i(1, 0)));
});
@@ -74,10 +75,10 @@ await server.WaitAssertion(() =>
{
var grid = entityManager.GetComponent(gridId);
var solution = new Solution("Blood", FixedPoint2.New(100));
- var tileRef = grid.GetTileRef(puddleOrigin);
+ var tileRef = mapSystem.GetTileRef(gridId, grid, puddleOrigin);
#pragma warning disable NUnit2045 // Interdependent tests
Assert.That(puddleSystem.TrySpillAt(tileRef, solution, out _), Is.True);
- Assert.That(GetPuddle(entityManager, grid, puddleOrigin), Is.Not.Null);
+ Assert.That(GetPuddle(entityManager, (gridId, grid), puddleOrigin), Is.Not.Null);
#pragma warning restore NUnit2045
});
@@ -87,7 +88,7 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() =>
{
var grid = entityManager.GetComponent(gridId);
- var puddle = GetPuddleEntity(entityManager, grid, puddleOrigin);
+ var puddle = GetPuddleEntity(entityManager, (gridId, grid), puddleOrigin);
#pragma warning disable NUnit2045 // Interdependent tests
Assert.That(puddle, Is.Not.Null);
@@ -104,7 +105,7 @@ await server.WaitAssertion(() =>
}
var newPos = new Vector2i(x, y);
- var sidePuddle = GetPuddle(entityManager, grid, newPos);
+ var sidePuddle = GetPuddle(entityManager, (gridId, grid), newPos);
Assert.That(sidePuddle, Is.Null);
}
}
diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
index a9069892dffdb6..ee2d0cb1f7a7fb 100644
--- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
+++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
@@ -5,7 +5,6 @@
using Content.Shared.Fluids.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
namespace Content.IntegrationTests.Tests.Fluids
{
@@ -21,8 +20,7 @@ public async Task TilePuddleTest()
var testMap = await pair.CreateTestMap();
- var entitySystemManager = server.ResolveDependency();
- var spillSystem = entitySystemManager.GetEntitySystem();
+ var spillSystem = server.System();
await server.WaitAssertion(() =>
{
@@ -46,17 +44,19 @@ public async Task SpaceNoPuddleTest()
var server = pair.Server;
var testMap = await pair.CreateTestMap();
- var grid = testMap.Grid.Comp;
+ var grid = testMap.Grid;
var entitySystemManager = server.ResolveDependency();
- var spillSystem = entitySystemManager.GetEntitySystem();
+ var spillSystem = server.System();
+ var mapSystem = server.System();
// Remove all tiles
await server.WaitPost(() =>
{
- foreach (var tile in grid.GetAllTiles())
+ var tiles = mapSystem.GetAllTiles(grid.Owner, grid.Comp);
+ foreach (var tile in tiles)
{
- grid.SetTile(tile.GridIndices, Tile.Empty);
+ mapSystem.SetTile(grid, tile.GridIndices, Tile.Empty);
}
});
diff --git a/Content.IntegrationTests/Tests/FollowerSystemTest.cs b/Content.IntegrationTests/Tests/FollowerSystemTest.cs
index 4d308c6d911f67..f4447426c771a6 100644
--- a/Content.IntegrationTests/Tests/FollowerSystemTest.cs
+++ b/Content.IntegrationTests/Tests/FollowerSystemTest.cs
@@ -22,6 +22,7 @@ public async Task FollowerMapDeleteTest()
var mapMan = server.ResolveDependency();
var sysMan = server.ResolveDependency();
var logMan = server.ResolveDependency();
+ var mapSys = server.System();
var logger = logMan.RootSawmill;
await server.WaitPost(() =>
@@ -29,7 +30,7 @@ await server.WaitPost(() =>
var followerSystem = sysMan.GetEntitySystem();
// Create a map to spawn the observers on.
- var map = mapMan.CreateMap();
+ mapSys.CreateMap(out var map);
// Spawn an observer to be followed.
var followed = entMan.SpawnEntity(GameTicker.ObserverPrototypeName, new MapCoordinates(0, 0, map));
@@ -41,7 +42,7 @@ await server.WaitPost(() =>
followerSystem.StartFollowingEntity(follower, followed);
- entMan.DeleteEntity(mapMan.GetMapEntityId(map));
+ entMan.DeleteEntity(mapSys.GetMap(map));
});
await pair.CleanReturnAsync();
}
diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
index 0ac6b68a3ec275..2570e2246a66f7 100644
--- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
+++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
@@ -1,5 +1,4 @@
#nullable enable
-using System.Numerics;
using Content.Server.Cuffs;
using Content.Shared.Body.Components;
using Content.Shared.Cuffs.Components;
@@ -7,7 +6,6 @@
using Robust.Server.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
-using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{
@@ -52,10 +50,11 @@ public async Task Test()
var mapManager = server.ResolveDependency();
var host = server.ResolveDependency();
+ var map = await pair.CreateTestMap();
+
await server.WaitAssertion(() =>
{
- var mapId = mapManager.CreateMap();
- var coordinates = new MapCoordinates(Vector2.Zero, mapId);
+ var coordinates = map.MapCoords;
var cuffableSys = entityManager.System();
var xformSys = entityManager.System();
diff --git a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs
index 1bea33a82bcd50..b215584c57aa7f 100644
--- a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs
+++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs
@@ -47,7 +47,7 @@ public async Task TestLobbyPlayersValid()
Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True);
// By default, traitor/antag preferences are disabled, so the pool should be empty.
- var sessions = new List{pair.Player!};
+ var sessions = new List { pair.Player! };
var pool = sys.GetPlayerPool(rule, sessions, def);
Assert.That(pool.Count, Is.EqualTo(0));
diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs
index f660eccf30ad91..518166ed864480 100644
--- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs
+++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs
@@ -110,7 +110,7 @@ public async Task FailAndStartTest()
player = pair.Player!.AttachedEntity!.Value;
Assert.That(entMan.EntityExists(player));
- ticker.SetGamePreset((GamePresetPrototype?)null);
+ ticker.SetGamePreset((GamePresetPrototype?) null);
server.CfgMan.SetCVar(CCVars.GridFill, false);
server.CfgMan.SetCVar(CCVars.GameLobbyFallbackEnabled, true);
server.CfgMan.SetCVar(CCVars.GameLobbyDefaultPreset, "secret");
diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs
index fc50d0bd334f14..7b3c9943029d68 100644
--- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs
+++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs
@@ -7,6 +7,7 @@
using Content.Server.Mind;
using Content.Server.Pinpointer;
using Content.Server.Roles;
+using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
@@ -49,6 +50,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing()
var roleSys = server.System();
var invSys = server.System();
var factionSys = server.System();
+ var roundEndSys = server.System();
server.CfgMan.SetCVar(CCVars.GridFill, true);
@@ -63,11 +65,11 @@ public async Task TryStopNukeOpsFromConstantlyFailing()
// Opt into the nukies role.
await pair.SetAntagPreference("NukeopsCommander", true);
- await pair.SetAntagPreference( "NukeopsMedic", true, dummies[1].UserId);
+ await pair.SetAntagPreference("NukeopsMedic", true, dummies[1].UserId);
// Initially, the players have no attached entities
Assert.That(pair.Player?.AttachedEntity, Is.Null);
- Assert.That(dummies.All(x => x.AttachedEntity == null));
+ Assert.That(dummies.All(x => x.AttachedEntity == null));
// There are no grids or maps
Assert.That(entMan.Count(), Is.Zero);
@@ -150,7 +152,8 @@ void CheckDummy(int i)
}
// The game rule exists, and all the stations/shuttles/maps are properly initialized
- var rule = entMan.AllComponents().Single().Component;
+ var rule = entMan.AllComponents().Single();
+ var ruleComp = rule.Component;
var gridsRule = entMan.AllComponents().Single().Component;
foreach (var grid in gridsRule.MapGrids)
{
@@ -158,12 +161,14 @@ void CheckDummy(int i)
Assert.That(entMan.HasComponent(grid));
Assert.That(entMan.HasComponent(grid));
}
- Assert.That(entMan.EntityExists(rule.TargetStation));
+ Assert.That(entMan.EntityExists(ruleComp.TargetStation));
- Assert.That(entMan.HasComponent(rule.TargetStation));
+ Assert.That(entMan.HasComponent(ruleComp.TargetStation));
- var nukieShuttlEnt = entMan.AllComponents().FirstOrDefault().Uid;
+ var nukieShuttle = entMan.AllComponents().Single();
+ var nukieShuttlEnt = nukieShuttle.Uid;
Assert.That(entMan.EntityExists(nukieShuttlEnt));
+ Assert.That(nukieShuttle.Component.AssociatedRule, Is.EqualTo(rule.Uid));
EntityUid? nukieStationEnt = null;
foreach (var grid in gridsRule.MapGrids)
@@ -179,12 +184,12 @@ void CheckDummy(int i)
var nukieStation = entMan.GetComponent(nukieStationEnt!.Value);
Assert.That(entMan.EntityExists(nukieStation.Station));
- Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation));
+ Assert.That(nukieStation.Station, Is.Not.EqualTo(ruleComp.TargetStation));
Assert.That(server.MapMan.MapExists(gridsRule.Map));
var nukieMap = mapSys.GetMap(gridsRule.Map!.Value);
- var targetStation = entMan.GetComponent(rule.TargetStation!.Value);
+ var targetStation = entMan.GetComponent(ruleComp.TargetStation!.Value);
var targetGrid = targetStation.Grids.First();
var targetMap = entMan.GetComponent(targetGrid).MapUid!.Value;
Assert.That(targetMap, Is.Not.EqualTo(nukieMap));
@@ -206,7 +211,7 @@ void CheckDummy(int i)
Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(nukieStationEnt.Value), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized));
- Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
+ Assert.That(LifeStage(ruleComp.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
// Make sure the player has hands. We've had fucking disarmed nukies before.
Assert.That(entMan.HasComponent(player));
@@ -222,10 +227,10 @@ void CheckDummy(int i)
}
Assert.That(total, Is.GreaterThan(3));
- // Finally lets check the nukie commander passed basic training and figured out how to breathe.
+ // Check the nukie commander passed basic training and figured out how to breathe.
var totalSeconds = 30;
var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
- int increment = 5;
+ var increment = 5;
var resp = entMan.GetComponent(player);
var damage = entMan.GetComponent(player);
for (var tick = 0; tick < totalTicks; tick += increment)
@@ -235,7 +240,24 @@ void CheckDummy(int i)
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
}
- ticker.SetGamePreset((GamePresetPrototype?)null);
+ // Check that the round does not end prematurely when agents are deleted in the outpost
+ var nukies = dummyEnts.Where(entMan.HasComponent).Append(player).ToArray();
+ await server.WaitAssertion(() =>
+ {
+ for (var i = 0; i < nukies.Length - 1; i++)
+ {
+ entMan.DeleteEntity(nukies[i]);
+ Assert.That(roundEndSys.IsRoundEndRequested, Is.False,
+ $"The round ended, but {nukies.Length - i - 1} nukies are still alive!");
+ }
+ // Delete the last nukie and make sure the round ends.
+ entMan.DeleteEntity(nukies[^1]);
+
+ Assert.That(roundEndSys.IsRoundEndRequested,
+ "All nukies were deleted, but the round didn't end!");
+ });
+
+ ticker.SetGamePreset((GamePresetPrototype?) null);
await pair.CleanReturnAsync();
}
}
diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs
index 7f817e8a1e0e1a..64f7a6d082085d 100644
--- a/Content.IntegrationTests/Tests/GravityGridTest.cs
+++ b/Content.IntegrationTests/Tests/GravityGridTest.cs
@@ -34,29 +34,25 @@ public async Task Test()
var testMap = await pair.CreateTestMap();
- EntityUid generator = default;
- var entityMan = server.ResolveDependency();
- var mapMan = server.ResolveDependency();
+ var entityMan = server.EntMan;
+ var mapMan = server.MapMan;
var mapSys = entityMan.System();
- MapGridComponent grid1 = null;
- MapGridComponent grid2 = null;
- EntityUid grid1Entity = default!;
- EntityUid grid2Entity = default!;
+ EntityUid generator = default;
+ Entity grid1 = default;
+ Entity grid2 = default;
// Create grids
await server.WaitAssertion(() =>
{
var mapId = testMap.MapId;
- grid1 = mapMan.CreateGrid(mapId);
- grid2 = mapMan.CreateGrid(mapId);
- grid1Entity = grid1.Owner;
- grid2Entity = grid2.Owner;
+ grid1 = mapMan.CreateGridEntity(mapId);
+ grid2 = mapMan.CreateGridEntity(mapId);
- mapSys.SetTile(grid1Entity, grid1, Vector2i.Zero, new Tile(1));
- mapSys.SetTile(grid2Entity, grid2, Vector2i.Zero, new Tile(1));
+ mapSys.SetTile(grid1, grid1, Vector2i.Zero, new Tile(1));
+ mapSys.SetTile(grid2, grid2, Vector2i.Zero, new Tile(1));
- generator = entityMan.SpawnEntity("GridGravityGeneratorDummy", new EntityCoordinates(grid1Entity, 0.5f, 0.5f));
+ generator = entityMan.SpawnEntity("GridGravityGeneratorDummy", new EntityCoordinates(grid1, 0.5f, 0.5f));
Assert.Multiple(() =>
{
Assert.That(entityMan.HasComponent(generator));
@@ -77,8 +73,8 @@ await server.WaitAssertion(() =>
Assert.Multiple(() =>
{
Assert.That(generatorComponent.GravityActive, Is.True);
- Assert.That(!entityMan.GetComponent(grid1Entity).EnabledVV);
- Assert.That(entityMan.GetComponent(grid2Entity).EnabledVV);
+ Assert.That(!entityMan.GetComponent(grid1).EnabledVV);
+ Assert.That(entityMan.GetComponent(grid2).EnabledVV);
});
// Re-enable needs power so it turns off again.
@@ -95,7 +91,7 @@ await server.WaitAssertion(() =>
Assert.Multiple(() =>
{
Assert.That(generatorComponent.GravityActive, Is.False);
- Assert.That(entityMan.GetComponent