diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml
new file mode 100644
index 00000000000..0afcab734ff
--- /dev/null
+++ b/.github/workflows/check-crlf.yml
@@ -0,0 +1,15 @@
+name: CRLF Check
+
+on:
+ pull_request:
+ types: [ opened, reopened, synchronize, ready_for_review ]
+
+jobs:
+ build:
+ name: CRLF Check
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3.6.0
+ - name: Check for CRLF
+ run: Tools/check_crlf.py
diff --git a/.github/workflows/close-master-pr.yml b/.github/workflows/close-master-pr.yml
index 66843d35dd6..51ce874dd07 100644
--- a/.github/workflows/close-master-pr.yml
+++ b/.github/workflows/close-master-pr.yml
@@ -1,4 +1,4 @@
-name: Close PR's on master
+name: Close PRs on master
on:
pull_request_target:
diff --git a/.github/workflows/labeler-untriaged.yml b/.github/workflows/labeler-untriaged.yml
new file mode 100644
index 00000000000..630122aa087
--- /dev/null
+++ b/.github/workflows/labeler-untriaged.yml
@@ -0,0 +1,13 @@
+name: "Labels: Untriaged"
+
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ add_label:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions-ecosystem/action-add-labels@v1
+ with:
+ labels: "Status: Untriaged"
diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml b/Content.Client/Access/UI/AccessLevelControl.xaml
new file mode 100644
index 00000000000..56968d89839
--- /dev/null
+++ b/Content.Client/Access/UI/AccessLevelControl.xaml
@@ -0,0 +1,4 @@
+
+
diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml.cs b/Content.Client/Access/UI/AccessLevelControl.xaml.cs
new file mode 100644
index 00000000000..34db80b7af9
--- /dev/null
+++ b/Content.Client/Access/UI/AccessLevelControl.xaml.cs
@@ -0,0 +1,52 @@
+using System.Linq;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Content.Shared.Access;
+using Content.Shared.Access.Systems;
+
+namespace Content.Client.Access.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class AccessLevelControl : GridContainer
+{
+ public readonly Dictionary, Button> ButtonsList = new();
+
+ public AccessLevelControl()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public void Populate(List> accessLevels, IPrototypeManager prototypeManager)
+ {
+ foreach (var access in accessLevels)
+ {
+ if (!prototypeManager.TryIndex(access, out var accessLevel))
+ {
+ Logger.Error($"Unable to find accesslevel for {access}");
+ continue;
+ }
+
+ var newButton = new Button
+ {
+ Text = accessLevel.GetAccessLevelName(),
+ ToggleMode = true,
+ };
+ AddChild(newButton);
+ ButtonsList.Add(accessLevel.ID, newButton);
+ }
+ }
+
+ public void UpdateState(
+ List> pressedList,
+ List>? enabledList = null)
+ {
+ foreach (var (accessName, button) in ButtonsList)
+ {
+ button.Pressed = pressedList.Contains(accessName);
+ button.Disabled = !(enabledList?.Contains(accessName) ?? true);
+ }
+ }
+}
diff --git a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs
index 0c23542f798..c1b63dc4d05 100644
--- a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs
+++ b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs
@@ -64,7 +64,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(castState);
}
- public void SubmitData(List newAccessList)
+ public void SubmitData(List> newAccessList)
{
SendMessage(new WriteToTargetAccessReaderIdMessage(newAccessList));
}
diff --git a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs
index 2fd00571215..6025c3b551f 100644
--- a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs
+++ b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs
@@ -16,7 +16,6 @@ public sealed partial class AccessOverriderWindow : DefaultWindow
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- private readonly ISawmill _logMill = default!;
private readonly AccessOverriderBoundUserInterface _owner;
private readonly Dictionary _accessButtons = new();
@@ -25,7 +24,7 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- _logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
+ var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
_owner = owner;
@@ -33,13 +32,13 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype
{
if (!prototypeManager.TryIndex(access, out var accessLevel))
{
- _logMill.Error($"Unable to find accesslevel for {access}");
+ logMill.Error($"Unable to find accesslevel for {access}");
continue;
}
var newButton = new Button
{
- Text = GetAccessLevelName(accessLevel),
+ Text = accessLevel.GetAccessLevelName(),
ToggleMode = true,
};
@@ -49,14 +48,6 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype
}
}
- private static string GetAccessLevelName(AccessLevelPrototype prototype)
- {
- if (prototype.Name is { } name)
- return Loc.GetString(name);
-
- return prototype.ID;
- }
-
public void UpdateState(AccessOverriderBoundUserInterfaceState state)
{
PrivilegedIdLabel.Text = state.PrivilegedIdName;
@@ -105,7 +96,7 @@ private void SubmitData()
_owner.SubmitData(
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
- _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
+ _accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId(x.Key)).ToList());
}
}
}
diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
index 898792aa030..5b7011c195a 100644
--- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
+++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Access;
using Content.Shared.Access.Components;
+using Content.Shared.Access;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest;
@@ -28,7 +29,6 @@ protected override void Open()
if (EntMan.TryGetComponent(Owner, out var idCard))
{
accessLevels = idCard.AccessLevels;
- accessLevels.Sort();
}
else
{
@@ -65,7 +65,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(castState);
}
- public void SubmitData(string newFullName, string newJobTitle, List newAccessList, string newJobPrototype)
+ public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, string newJobPrototype)
{
if (newFullName.Length > MaxFullNameLength)
newFullName = newFullName[..MaxFullNameLength];
diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml b/Content.Client/Access/UI/IdCardConsoleWindow.xaml
index c29adc8ebd3..a2f5f3382bb 100644
--- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml
+++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml
@@ -30,10 +30,6 @@
-
-
-
-
-
+
diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
index bf5984e8096..298912e7d53 100644
--- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
+++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
@@ -20,7 +20,7 @@ public sealed partial class IdCardConsoleWindow : DefaultWindow
private readonly IdCardConsoleBoundUserInterface _owner;
- private readonly Dictionary _accessButtons = new();
+ private AccessLevelControl _accessButtons = new();
private readonly List _jobPrototypeIds = new();
private string? _lastFullName;
@@ -66,36 +66,18 @@ public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeMana
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
- foreach (var access in accessLevels)
- {
- if (!prototypeManager.TryIndex(access, out var accessLevel))
- {
- _logMill.Error($"Unable to find accesslevel for {access}");
- continue;
- }
+ _accessButtons.Populate(accessLevels, prototypeManager);
+ AccessLevelControlContainer.AddChild(_accessButtons);
- var newButton = new Button
- {
- Text = GetAccessLevelName(accessLevel),
- ToggleMode = true,
- };
- AccessLevelGrid.AddChild(newButton);
- _accessButtons.Add(accessLevel.ID, newButton);
- newButton.OnPressed += _ => SubmitData();
+ foreach (var (id, button) in _accessButtons.ButtonsList)
+ {
+ button.OnPressed += _ => SubmitData();
}
}
- private static string GetAccessLevelName(AccessLevelPrototype prototype)
- {
- if (prototype.Name is { } name)
- return Loc.GetString(name);
-
- return prototype.ID;
- }
-
private void ClearAllAccess()
{
- foreach (var button in _accessButtons.Values)
+ foreach (var button in _accessButtons.ButtonsList.Values)
{
if (button.Pressed)
{
@@ -119,7 +101,7 @@ private void SelectJobPreset(OptionButton.ItemSelectedEventArgs args)
// this is a sussy way to do this
foreach (var access in job.Access)
{
- if (_accessButtons.TryGetValue(access, out var button) && !button.Disabled)
+ if (_accessButtons.ButtonsList.TryGetValue(access, out var button) && !button.Disabled)
{
button.Pressed = true;
}
@@ -134,7 +116,7 @@ private void SelectJobPreset(OptionButton.ItemSelectedEventArgs args)
foreach (var access in groupPrototype.Tags)
{
- if (_accessButtons.TryGetValue(access, out var button) && !button.Disabled)
+ if (_accessButtons.ButtonsList.TryGetValue(access, out var button) && !button.Disabled)
{
button.Pressed = true;
}
@@ -184,15 +166,10 @@ public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
JobPresetOptionButton.Disabled = !interfaceEnabled;
- foreach (var (accessName, button) in _accessButtons)
- {
- button.Disabled = !interfaceEnabled;
- if (interfaceEnabled)
- {
- button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
- button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
- }
- }
+ _accessButtons.UpdateState(state.TargetIdAccessList?.ToList() ??
+ new List>(),
+ state.AllowedModifyAccessList?.ToList() ??
+ new List>());
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
if (jobIndex >= 0)
@@ -215,7 +192,7 @@ private void SubmitData()
FullNameLineEdit.Text,
JobTitleLineEdit.Text,
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
- _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
+ _accessButtons.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty);
}
}
diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs
index 31d092b25d1..b992e772563 100644
--- a/Content.Client/Actions/ActionsSystem.cs
+++ b/Content.Client/Actions/ActionsSystem.cs
@@ -95,6 +95,7 @@ private void BaseHandleState(EntityUid uid, BaseActionComponent component, Ba
component.Container = EnsureEntity(state.Container, uid);
component.EntityIcon = EnsureEntity(state.EntityIcon, uid);
component.CheckCanInteract = state.CheckCanInteract;
+ component.CheckConsciousness = state.CheckConsciousness;
component.ClientExclusive = state.ClientExclusive;
component.Priority = state.Priority;
component.AttachedEntity = EnsureEntity(state.AttachedEntity, uid);
diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs b/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs
index 06eace118d7..502c56a5a69 100644
--- a/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs
+++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs
@@ -2,6 +2,7 @@
using Content.Shared.Administration.Notes;
using Content.Shared.Eui;
using JetBrains.Annotations;
+using Robust.Client.UserInterface.Controls;
using static Content.Shared.Administration.Notes.AdminMessageEuiMsg;
namespace Content.Client.Administration.UI.AdminRemarks;
@@ -14,9 +15,8 @@ public sealed class AdminMessageEui : BaseEui
public AdminMessageEui()
{
_popup = new AdminMessagePopupWindow();
- _popup.OnAcceptPressed += () => SendMessage(new Accept());
- _popup.OnDismissPressed += () => SendMessage(new Dismiss());
- _popup.OnClose += () => SendMessage(new CloseEuiMessage());
+ _popup.OnAcceptPressed += () => SendMessage(new Dismiss(true));
+ _popup.OnDismissPressed += () => SendMessage(new Dismiss(false));
}
public override void HandleState(EuiStateBase state)
@@ -26,13 +26,17 @@ public override void HandleState(EuiStateBase state)
return;
}
- _popup.SetMessage(s.Message);
- _popup.SetDetails(s.AdminName, s.AddedOn);
- _popup.Timer = s.Time;
+ _popup.SetState(s);
}
public override void Opened()
{
- _popup.OpenCentered();
+ _popup.UserInterfaceManager.WindowRoot.AddChild(_popup);
+ LayoutContainer.SetAnchorPreset(_popup, LayoutContainer.LayoutPreset.Wide);
+ }
+
+ public override void Closed()
+ {
+ _popup.Orphan();
}
}
diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml
new file mode 100644
index 00000000000..9a60a6f72ab
--- /dev/null
+++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs
new file mode 100644
index 00000000000..7bb425f618e
--- /dev/null
+++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Administration.Notes;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Administration.UI.AdminRemarks;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminMessagePopupMessage : Control
+{
+ public AdminMessagePopupMessage(AdminMessageEuiState.Message message)
+ {
+ RobustXamlLoader.Load(this);
+
+ Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
+ "admin-notes-message-admin",
+ ("admin", message.AdminName),
+ ("date", message.AddedOn.ToLocalTime()))));
+
+ Message.SetMessage(message.Text);
+ }
+}
diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml
index 311829e8b2b..cc5207bb3a0 100644
--- a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml
+++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml
@@ -1,22 +1,36 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Text="{Loc 'admin-notes-message-dismiss'}"
+ Disabled="True"
+ HorizontalExpand="True"
+ StyleClasses="OpenRight" />
+ Disabled="True"
+ HorizontalExpand="True"
+ StyleClasses="OpenLeft" />
-
+
-
+
diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml.cs b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml.cs
index f28fd1b60b1..bf2ca9bec41 100644
--- a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml.cs
@@ -1,56 +1,65 @@
-using Content.Client.UserInterface.Controls;
+using Content.Client.Stylesheets;
+using Content.Shared.Administration.Notes;
using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.AdminRemarks;
[GenerateTypedNameReferences]
-public sealed partial class AdminMessagePopupWindow : FancyWindow
+public sealed partial class AdminMessagePopupWindow : Control
{
private float _timer = float.MaxValue;
- public float Timer
- {
- get => _timer;
- set
- {
- WaitLabel.Text = Loc.GetString("admin-notes-message-wait", ("time", MathF.Floor(value)));
- _timer = value;
- }
- }
public event Action? OnDismissPressed;
+
public event Action? OnAcceptPressed;
public AdminMessagePopupWindow()
{
RobustXamlLoader.Load(this);
+ Stylesheet = IoCManager.Resolve().SheetSpace;
+
AcceptButton.OnPressed += OnAcceptButtonPressed;
DismissButton.OnPressed += OnDismissButtonPressed;
}
- public void SetMessage(string message)
+ public float Timer
{
- MessageLabel.SetMessage(message);
+ get => _timer;
+ private set
+ {
+ WaitLabel.Text = Loc.GetString("admin-notes-message-wait", ("time", MathF.Floor(value)));
+ _timer = value;
+ }
}
- public void SetDetails(string adminName, DateTime addedOn)
+ public void SetState(AdminMessageEuiState state)
{
- AdminLabel.Text = Loc.GetString("admin-notes-message-admin", ("admin", adminName), ("date", addedOn));
+ Timer = (float) state.Time.TotalSeconds;
+
+ MessageContainer.RemoveAllChildren();
+
+ foreach (var message in state.Messages)
+ {
+ MessageContainer.AddChild(new AdminMessagePopupMessage(message));
+ }
+
+ Description.SetMessage(FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
}
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
{
OnDismissPressed?.Invoke();
- Close();
}
private void OnAcceptButtonPressed(BaseButton.ButtonEventArgs obj)
{
OnAcceptPressed?.Invoke();
- Close();
}
protected override void FrameUpdate(FrameEventArgs args)
@@ -70,6 +79,7 @@ protected override void FrameUpdate(FrameEventArgs args)
else
{
AcceptButton.Disabled = false;
+ DismissButton.Disabled = false;
}
}
}
diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs
index 237f24e3eae..223bf7876ac 100644
--- a/Content.Client/Alerts/ClientAlertsSystem.cs
+++ b/Content.Client/Alerts/ClientAlertsSystem.cs
@@ -49,24 +49,23 @@ public IReadOnlyDictionary? ActiveAlerts
protected override void AfterShowAlert(Entity alerts)
{
- if (_playerManager.LocalEntity != alerts.Owner)
- return;
-
- SyncAlerts?.Invoke(this, alerts.Comp.Alerts);
+ UpdateHud(alerts);
}
- protected override void AfterClearAlert(Entity alertsComponent)
+ protected override void AfterClearAlert(Entity alerts)
{
- if (_playerManager.LocalEntity != alertsComponent.Owner)
- return;
+ UpdateHud(alerts);
+ }
- SyncAlerts?.Invoke(this, alertsComponent.Comp.Alerts);
+ private void ClientAlertsHandleState(Entity alerts, ref AfterAutoHandleStateEvent args)
+ {
+ UpdateHud(alerts);
}
- private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref AfterAutoHandleStateEvent args)
+ private void UpdateHud(Entity entity)
{
- if (_playerManager.LocalEntity == uid)
- SyncAlerts?.Invoke(this, component.Alerts);
+ if (_playerManager.LocalEntity == entity.Owner)
+ SyncAlerts?.Invoke(this, entity.Comp.Alerts);
}
private void OnPlayerAttached(EntityUid uid, AlertsComponent component, LocalPlayerAttachedEvent args)
diff --git a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs
new file mode 100644
index 00000000000..4f182c458cc
--- /dev/null
+++ b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs
@@ -0,0 +1,21 @@
+using Content.Shared.Alert;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Alerts;
+
+///
+/// Event raised on an entity with alerts in order to allow it to update visuals for the alert sprite entity.
+///
+[ByRefEvent]
+public record struct UpdateAlertSpriteEvent
+{
+ public Entity SpriteViewEnt;
+
+ public AlertPrototype Alert;
+
+ public UpdateAlertSpriteEvent(Entity spriteViewEnt, AlertPrototype alert)
+ {
+ SpriteViewEnt = spriteViewEnt;
+ Alert = alert;
+ }
+}
diff --git a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml
index ac4adf7e0e4..36a750d0098 100644
--- a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml
+++ b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml
@@ -1,9 +1,9 @@
-
+ MinSize="350 400"
+ SetSize="350 400">
diff --git a/Content.Client/Antag/AntagStatusIconSystem.cs b/Content.Client/Antag/AntagStatusIconSystem.cs
index 5d87837893c..804ae21ad4a 100644
--- a/Content.Client/Antag/AntagStatusIconSystem.cs
+++ b/Content.Client/Antag/AntagStatusIconSystem.cs
@@ -22,6 +22,7 @@ public override void Initialize()
SubscribeLocalEvent(GetRevIcon);
SubscribeLocalEvent(GetIcon);
SubscribeLocalEvent(GetIcon);
+ SubscribeLocalEvent(GetIcon);
}
///
diff --git a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs
index fcf3b04e530..6dfbc326ecb 100644
--- a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs
+++ b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs
@@ -256,6 +256,8 @@ private void DrawTooltip(DrawingHandleScreen handle, Vector2 pos, AtmosDebugOver
handle.DrawString(_font, pos, $"Map: {data.MapAtmosphere}");
pos += offset;
handle.DrawString(_font, pos, $"NoGrid: {data.NoGrid}");
+ pos += offset;
+ handle.DrawString(_font, pos, $"Immutable: {data.Immutable}");
}
private void GetGrids(MapId mapId, Box2Rotated box)
diff --git a/Content.Client/Atmos/Overlays/GasTileOverlay.cs b/Content.Client/Atmos/Overlays/GasTileOverlay.cs
index ef65d43fe85..f4dc274a4e5 100644
--- a/Content.Client/Atmos/Overlays/GasTileOverlay.cs
+++ b/Content.Client/Atmos/Overlays/GasTileOverlay.cs
@@ -8,7 +8,6 @@
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
-using Robust.Shared.Graphics;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -23,7 +22,7 @@ public sealed class GasTileOverlay : Overlay
private readonly IEntityManager _entManager;
private readonly IMapManager _mapManager;
- public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;
+ public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
private readonly ShaderInstance _shader;
// Gas overlays
@@ -79,7 +78,8 @@ public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IR
var rsi = resourceCache.GetResource(animated.RsiPath).RSI;
var stateId = animated.RsiState;
- if (!rsi.TryGetState(stateId, out var state)) continue;
+ if (!rsi.TryGetState(stateId, out var state))
+ continue;
_frames[i] = state.GetFrames(RsiDirection.South);
_frameDelays[i] = state.GetDelays();
@@ -111,7 +111,8 @@ protected override void FrameUpdate(FrameEventArgs args)
for (var i = 0; i < _gasCount; i++)
{
var delays = _frameDelays[i];
- if (delays.Length == 0) continue;
+ if (delays.Length == 0)
+ continue;
var frameCount = _frameCounter[i];
_timer[i] += args.DeltaSeconds;
@@ -127,7 +128,8 @@ protected override void FrameUpdate(FrameEventArgs args)
for (var i = 0; i < FireStates; i++)
{
var delays = _fireFrameDelays[i];
- if (delays.Length == 0) continue;
+ if (delays.Length == 0)
+ continue;
var frameCount = _fireFrameCounter[i];
_fireTimer[i] += args.DeltaSeconds;
@@ -161,26 +163,10 @@ protected override void Draw(in OverlayDrawArgs args)
var mapUid = _mapManager.GetMapEntityId(args.MapId);
if (_entManager.TryGetComponent(mapUid, out var atmos))
- {
- var bottomLeft = args.WorldAABB.BottomLeft.Floored();
- var topRight = args.WorldAABB.TopRight.Ceiled();
-
- for (var x = bottomLeft.X; x <= topRight.X; x++)
- {
- for (var y = bottomLeft.Y; y <= topRight.Y; y++)
- {
- var tilePosition = new Vector2(x, y);
-
- for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
- {
- var opacity = atmos.OverlayData.Opacity[i];
+ DrawMapOverlay(drawHandle, args, mapUid, atmos);
- if (opacity > 0)
- args.WorldHandle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
- }
- }
- }
- }
+ if (args.Space != OverlaySpace.WorldSpaceEntities)
+ return;
// TODO: WorldBounds callback.
_mapManager.FindGridsIntersecting(args.MapId, args.WorldAABB, ref gridState,
@@ -265,5 +251,41 @@ protected override void Draw(in OverlayDrawArgs args)
drawHandle.UseShader(null);
drawHandle.SetTransform(Matrix3.Identity);
}
+
+ private void DrawMapOverlay(
+ DrawingHandleWorld handle,
+ OverlayDrawArgs args,
+ EntityUid map,
+ MapAtmosphereComponent atmos)
+ {
+ var mapGrid = _entManager.HasComponent(map);
+
+ // map-grid atmospheres get drawn above grids
+ if (mapGrid && args.Space != OverlaySpace.WorldSpaceEntities)
+ return;
+
+ // Normal map atmospheres get drawn below grids
+ if (!mapGrid && args.Space != OverlaySpace.WorldSpaceBelowWorld)
+ return;
+
+ var bottomLeft = args.WorldAABB.BottomLeft.Floored();
+ var topRight = args.WorldAABB.TopRight.Ceiled();
+
+ for (var x = bottomLeft.X; x <= topRight.X; x++)
+ {
+ for (var y = bottomLeft.Y; y <= topRight.Y; y++)
+ {
+ var tilePosition = new Vector2(x, y);
+
+ for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
+ {
+ var opacity = atmos.OverlayData.Opacity[i];
+
+ if (opacity > 0)
+ handle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
+ }
+ }
+ }
+ }
}
}
diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs
index 9d30cabb1ec..0206017baef 100644
--- a/Content.Client/Audio/AmbientSoundSystem.cs
+++ b/Content.Client/Audio/AmbientSoundSystem.cs
@@ -50,7 +50,6 @@ protected override void QueueUpdate(EntityUid uid, AmbientSoundComponent ambienc
private static AudioParams _params = AudioParams.Default
.WithVariation(0.01f)
.WithLoop(true)
- .WithAttenuation(Attenuation.LinearDistance)
.WithMaxDistance(7f);
///
diff --git a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs
index 0fdcc7a86dd..92c5b7a4191 100644
--- a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs
+++ b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs
@@ -23,8 +23,8 @@ public sealed partial class ContentAudioSystem
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
- private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
- private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
+ private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f);
+ private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, 0, 0, 0, false, 0f);
///
/// EntityUid of lobby restart sound component.
diff --git a/Content.Client/Chemistry/Components/HyposprayComponent.cs b/Content.Client/Chemistry/Components/HyposprayComponent.cs
deleted file mode 100644
index 705b79ad84c..00000000000
--- a/Content.Client/Chemistry/Components/HyposprayComponent.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Content.Shared.Chemistry.Components;
-using Content.Shared.FixedPoint;
-
-namespace Content.Client.Chemistry.Components
-{
- [RegisterComponent]
- public sealed partial class HyposprayComponent : SharedHyposprayComponent
- {
- [ViewVariables]
- public FixedPoint2 CurrentVolume;
- [ViewVariables]
- public FixedPoint2 TotalVolume;
- [ViewVariables(VVAccess.ReadWrite)]
- public bool UiUpdateNeeded;
- }
-}
diff --git a/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs
new file mode 100644
index 00000000000..ee7aa3aafe3
--- /dev/null
+++ b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs
@@ -0,0 +1,15 @@
+using Content.Client.Chemistry.UI;
+using Content.Client.Items;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+
+namespace Content.Client.Chemistry.EntitySystems;
+
+public sealed class HypospraySystem : SharedHypospraySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ Subs.ItemStatus(ent => new HyposprayStatusControl(ent, _solutionContainers));
+ }
+}
diff --git a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs
index 12eb7f3d14d..0131a283c8c 100644
--- a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs
+++ b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs
@@ -1,4 +1,3 @@
-using Content.Client.Chemistry.Components;
using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
@@ -13,17 +12,5 @@ public override void Initialize()
{
base.Initialize();
Subs.ItemStatus(ent => new InjectorStatusControl(ent, SolutionContainers));
- SubscribeLocalEvent(OnHandleHyposprayState);
- Subs.ItemStatus(ent => new HyposprayStatusControl(ent));
- }
-
- private void OnHandleHyposprayState(EntityUid uid, HyposprayComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not HyposprayComponentState cState)
- return;
-
- component.CurrentVolume = cState.CurVolume;
- component.TotalVolume = cState.MaxVolume;
- component.UiUpdateNeeded = true;
}
}
diff --git a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs
index bd85cd546cc..4a4d90dc4d5 100644
--- a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs
+++ b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs
@@ -1,6 +1,8 @@
-using Content.Client.Chemistry.Components;
using Content.Client.Message;
using Content.Client.Stylesheets;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.FixedPoint;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing;
@@ -9,34 +11,48 @@ namespace Content.Client.Chemistry.UI;
public sealed class HyposprayStatusControl : Control
{
- private readonly HyposprayComponent _parent;
+ private readonly Entity _parent;
private readonly RichTextLabel _label;
+ private readonly SharedSolutionContainerSystem _solutionContainers;
- public HyposprayStatusControl(HyposprayComponent parent)
+ private FixedPoint2 PrevVolume;
+ private FixedPoint2 PrevMaxVolume;
+ private bool PrevOnlyAffectsMobs;
+
+ public HyposprayStatusControl(Entity parent, SharedSolutionContainerSystem solutionContainers)
{
_parent = parent;
- _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
+ _solutionContainers = solutionContainers;
+ _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
-
- Update();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- if (!_parent.UiUpdateNeeded)
+
+ if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
return;
- Update();
- }
- public void Update()
- {
+ // only updates the UI if any of the details are different than they previously were
+ if (PrevVolume == solution.Volume
+ && PrevMaxVolume == solution.MaxVolume
+ && PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs)
+ return;
+
+ PrevVolume = solution.Volume;
+ PrevMaxVolume = solution.MaxVolume;
+ PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs;
- _parent.UiUpdateNeeded = false;
+ var modeStringLocalized = Loc.GetString(_parent.Comp.OnlyAffectsMobs switch
+ {
+ false => "hypospray-all-mode-text",
+ true => "hypospray-mobs-only-mode-text",
+ });
- _label.SetMarkup(Loc.GetString(
- "hypospray-volume-text",
- ("currentVolume", _parent.CurrentVolume),
- ("totalVolume", _parent.TotalVolume)));
+ _label.SetMarkup(Loc.GetString("hypospray-volume-label",
+ ("currentVolume", solution.Volume),
+ ("totalVolume", solution.MaxVolume),
+ ("modeString", modeStringLocalized)));
}
}
diff --git a/Content.Client/Chemistry/UI/InjectorStatusControl.cs b/Content.Client/Chemistry/UI/InjectorStatusControl.cs
index 9cb699330c2..ba1f97cd1e4 100644
--- a/Content.Client/Chemistry/UI/InjectorStatusControl.cs
+++ b/Content.Client/Chemistry/UI/InjectorStatusControl.cs
@@ -17,6 +17,7 @@ public sealed class InjectorStatusControl : Control
private FixedPoint2 PrevVolume;
private FixedPoint2 PrevMaxVolume;
+ private FixedPoint2 PrevTransferAmount;
private InjectorToggleMode PrevToggleState;
public InjectorStatusControl(Entity parent, SharedSolutionContainerSystem solutionContainers)
@@ -37,11 +38,13 @@ protected override void FrameUpdate(FrameEventArgs args)
// only updates the UI if any of the details are different than they previously were
if (PrevVolume == solution.Volume
&& PrevMaxVolume == solution.MaxVolume
+ && PrevTransferAmount == _parent.Comp.TransferAmount
&& PrevToggleState == _parent.Comp.ToggleState)
return;
PrevVolume = solution.Volume;
PrevMaxVolume = solution.MaxVolume;
+ PrevTransferAmount = _parent.Comp.TransferAmount;
PrevToggleState = _parent.Comp.ToggleState;
// Update current volume and injector state
diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs
index fbe9d5ec5bb..7e78ac7d707 100644
--- a/Content.Client/Clothing/ClientClothingSystem.cs
+++ b/Content.Client/Clothing/ClientClothingSystem.cs
@@ -133,7 +133,7 @@ private bool TryGetDefaultVisuals(EntityUid uid, ClothingComponent clothing, str
else if (TryComp(uid, out SpriteComponent? sprite))
rsi = sprite.BaseRSI;
- if (rsi == null || rsi.Path == null)
+ if (rsi == null)
return false;
var correctedSlot = slot;
diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs
index ae1724c3bf6..66000a8457d 100644
--- a/Content.Client/Construction/ConstructionSystem.cs
+++ b/Content.Client/Construction/ConstructionSystem.cs
@@ -27,6 +27,7 @@ public sealed class ConstructionSystem : SharedConstructionSystem
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
private readonly Dictionary _ghosts = new();
@@ -195,7 +196,7 @@ public bool TrySpawnGhost(
return false;
// This InRangeUnobstructed should probably be replaced with "is there something blocking us in that tile?"
- var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager));
+ var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem));
if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate))
return false;
diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs
index ae1b3ec3bf0..a60619baa35 100644
--- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs
+++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs
@@ -170,7 +170,7 @@ private bool HandleOpenEntityMenu(in PointerInputCmdHandler.PointerInputCmdArgs
if (_combatMode.IsInCombatMode(args.Session?.AttachedEntity))
return false;
- var coords = args.Coordinates.ToMap(_entityManager);
+ var coords = args.Coordinates.ToMap(_entityManager, _xform);
if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities))
OpenRootMenu(entities);
diff --git a/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs b/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs
new file mode 100644
index 00000000000..970af100a61
--- /dev/null
+++ b/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs
@@ -0,0 +1,26 @@
+using Content.Shared.DeltaV.Abilities.Borgs;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.DeltaV.Abilities.Borgs;
+
+///
+/// Responsible for coloring randomized candy.
+///
+public sealed class RandomizedCandyVisualizer : VisualizerSystem
+{
+ protected override void OnAppearanceChange(EntityUid uid, RandomizedCandyComponent component, ref AppearanceChangeEvent args)
+ {
+ if (!TryComp(uid, out var sprite)
+ || !AppearanceSystem.TryGetData(uid, RandomizedCandyVisuals.Color, out var color, args.Component))
+ {
+ return;
+ }
+
+ sprite.LayerSetColor(CandyVisualLayers.Ball, color);
+ }
+}
+
+public enum CandyVisualLayers : byte
+{
+ Ball
+}
diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs
index 344bd2ec979..b9e4a386604 100644
--- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs
+++ b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs
@@ -22,6 +22,9 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
private const string AnimationKey = "disposal_unit_animation";
+ private const string DefaultFlushState = "disposal-flush";
+ private const string DefaultChargeState = "disposal-charging";
+
public override void Initialize()
{
base.Initialize();
@@ -93,20 +96,24 @@ private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit,
private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance)
{
if (!_appearanceSystem.TryGetData(uid, Visuals.VisualState, out var state, appearance))
- {
return;
- }
sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored);
sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored);
- sprite.LayerSetVisible(DisposalUnitVisualLayers.BaseFlush, state is VisualState.Flushing or VisualState.Charging);
+ sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging);
+
+ var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer)
+ ? sprite.LayerGetState(chargingLayer)
+ : new RSI.StateId(DefaultChargeState);
// This is a transient state so not too worried about replaying in range.
- if (state == VisualState.Flushing)
+ if (state == VisualState.OverlayFlushing)
{
if (!_animationSystem.HasRunningAnimation(uid, AnimationKey))
{
- var flushState = new RSI.StateId("disposal-flush");
+ var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer)
+ ? sprite.LayerGetState(flushLayer)
+ : new RSI.StateId(DefaultFlushState);
// Setup the flush animation to play
var anim = new Animation
@@ -116,7 +123,7 @@ private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, Sprite
{
new AnimationTrackSpriteFlick
{
- LayerKey = DisposalUnitVisualLayers.BaseFlush,
+ LayerKey = DisposalUnitVisualLayers.OverlayFlush,
KeyFrames =
{
// Play the flush animation
@@ -124,7 +131,7 @@ private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, Sprite
// Return to base state (though, depending on how the unit is
// configured we might get an appearance change event telling
// us to go to charging state)
- new AnimationTrackSpriteFlick.KeyFrame("disposal-charging", (float) unit.FlushDelay.TotalSeconds)
+ new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) unit.FlushDelay.TotalSeconds)
}
},
}
@@ -145,26 +152,18 @@ private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, Sprite
_animationSystem.Play(uid, anim, AnimationKey);
}
}
- else if (state == VisualState.Charging)
- {
- sprite.LayerSetState(DisposalUnitVisualLayers.BaseFlush, new RSI.StateId("disposal-charging"));
- }
+ else if (state == VisualState.OverlayCharging)
+ sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, new RSI.StateId("disposal-charging"));
else
- {
_animationSystem.Stop(uid, AnimationKey);
- }
if (!_appearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState, appearance))
- {
handleState = HandleState.Normal;
- }
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal);
if (!_appearanceSystem.TryGetData(uid, Visuals.Light, out var lightState, appearance))
- {
lightState = LightStates.Off;
- }
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging,
(lightState & LightStates.Charging) != 0);
@@ -180,7 +179,7 @@ public enum DisposalUnitVisualLayers : byte
Unanchored,
Base,
BaseCharging,
- BaseFlush,
+ OverlayFlush,
OverlayCharging,
OverlayReady,
OverlayFull,
diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs
index 473ae97059a..bc52730b0e7 100644
--- a/Content.Client/Doors/DoorSystem.cs
+++ b/Content.Client/Doors/DoorSystem.cs
@@ -4,14 +4,12 @@
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
-using Robust.Shared.Timing;
namespace Content.Client.Doors;
public sealed class DoorSystem : SharedDoorSystem
{
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
public override void Initialize()
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
new file mode 100644
index 00000000000..cd7ea717ce3
--- /dev/null
+++ b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
@@ -0,0 +1,59 @@
+using Content.Shared.Access;
+using Content.Shared.Doors.Electronics;
+using Robust.Client.GameObjects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Doors.Electronics;
+
+public sealed class DoorElectronicsBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private DoorElectronicsConfigurationMenu? _window;
+
+ public DoorElectronicsBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+ List> accessLevels = new();
+
+ foreach (var accessLevel in _prototypeManager.EnumeratePrototypes())
+ {
+ if (accessLevel.Name != null)
+ {
+ accessLevels.Add(accessLevel.ID);
+ }
+ }
+
+ accessLevels.Sort();
+
+ _window = new DoorElectronicsConfigurationMenu(this, accessLevels, _prototypeManager);
+ _window.OnClose += Close;
+ _window.OpenCentered();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ var castState = (DoorElectronicsConfigurationState) state;
+
+ _window?.UpdateState(castState);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing) return;
+
+ _window?.Dispose();
+ }
+
+ public void UpdateConfiguration(List> newAccessList)
+ {
+ SendMessage(new DoorElectronicsUpdateConfigurationMessage(newAccessList));
+ }
+}
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml
new file mode 100644
index 00000000000..4cd59f38b23
--- /dev/null
+++ b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
new file mode 100644
index 00000000000..c01f13a462e
--- /dev/null
+++ b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
@@ -0,0 +1,41 @@
+using System.Linq;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Content.Client.Access.UI;
+using Content.Client.Doors.Electronics;
+using Content.Shared.Access;
+using Content.Shared.Doors.Electronics;
+using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
+
+namespace Content.Client.Doors.Electronics;
+
+[GenerateTypedNameReferences]
+public sealed partial class DoorElectronicsConfigurationMenu : FancyWindow
+{
+ private readonly DoorElectronicsBoundUserInterface _owner;
+ private AccessLevelControl _buttonsList = new();
+
+ public DoorElectronicsConfigurationMenu(DoorElectronicsBoundUserInterface ui, List> accessLevels, IPrototypeManager prototypeManager)
+ {
+ RobustXamlLoader.Load(this);
+
+ _owner = ui;
+
+ _buttonsList.Populate(accessLevels, prototypeManager);
+ AccessLevelControlContainer.AddChild(_buttonsList);
+
+ foreach (var (id, button) in _buttonsList.ButtonsList)
+ {
+ button.OnPressed += _ => _owner.UpdateConfiguration(
+ _buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
+ }
+ }
+
+ public void UpdateState(DoorElectronicsConfigurationState state)
+ {
+ _buttonsList.UpdateState(state.AccessList);
+ }
+}
diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index 3acc4ab1800..6cddeeb2038 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -70,6 +70,7 @@ public sealed class EntryPoint : GameClient
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IReplayLoadManager _replayLoad = default!;
[Dependency] private readonly ILogManager _logManager = default!;
+ [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!;
public override void Init()
{
@@ -100,14 +101,11 @@ public override void Init()
_prototypeManager.RegisterIgnore("seed"); // Seeds prototypes are server-only.
_prototypeManager.RegisterIgnore("objective");
_prototypeManager.RegisterIgnore("holiday");
- _prototypeManager.RegisterIgnore("aiFaction");
_prototypeManager.RegisterIgnore("htnCompound");
_prototypeManager.RegisterIgnore("htnPrimitive");
_prototypeManager.RegisterIgnore("gameMap");
_prototypeManager.RegisterIgnore("gameMapPool");
- _prototypeManager.RegisterIgnore("npcFaction");
_prototypeManager.RegisterIgnore("lobbyBackground");
- _prototypeManager.RegisterIgnore("advertisementsPack");
_prototypeManager.RegisterIgnore("gamePreset");
_prototypeManager.RegisterIgnore("noiseChannel");
_prototypeManager.RegisterIgnore("spaceBiome");
@@ -120,6 +118,7 @@ public override void Init()
_prototypeManager.RegisterIgnore("wireLayout");
_prototypeManager.RegisterIgnore("alertLevels");
_prototypeManager.RegisterIgnore("nukeopsRole");
+ _prototypeManager.RegisterIgnore("candyFlavor"); // Delta-V
_componentFactory.GenerateNetIds();
_adminManager.Initialize();
@@ -193,6 +192,7 @@ private void SwitchToDefaultState(bool disconnected = false)
_resourceManager,
ReplayConstants.ReplayZipFolder.ToRootedPath());
+ _replayMan.LastLoad = (null, ReplayConstants.ReplayZipFolder.ToRootedPath());
_replayLoad.LoadAndStartReplay(reader);
}
else if (_gameController.LaunchState.FromLauncher)
diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs
index 1be472b06d6..45db4efa53c 100644
--- a/Content.Client/Examine/ExamineSystem.cs
+++ b/Content.Client/Examine/ExamineSystem.cs
@@ -212,14 +212,16 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso
var vBox = new BoxContainer
{
Name = "ExaminePopupVbox",
- Orientation = LayoutOrientation.Vertical
+ Orientation = LayoutOrientation.Vertical,
+ MaxWidth = _examineTooltipOpen.MaxWidth
};
panel.AddChild(vBox);
var hBox = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
- SeparationOverride = 5
+ SeparationOverride = 5,
+ Margin = new Thickness(6, 0, 6, 0)
};
vBox.AddChild(hBox);
@@ -229,8 +231,7 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso
var spriteView = new SpriteView
{
OverrideDirection = Direction.South,
- SetSize = new Vector2(32, 32),
- Margin = new Thickness(2, 0, 2, 0),
+ SetSize = new Vector2(32, 32)
};
spriteView.SetEntity(target);
hBox.AddChild(spriteView);
@@ -238,19 +239,17 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso
if (knowTarget)
{
- hBox.AddChild(new Label
- {
- Text = Identity.Name(target, EntityManager, player),
- HorizontalExpand = true,
- });
+ var itemName = FormattedMessage.RemoveMarkup(Identity.Name(target, EntityManager, player));
+ var labelMessage = FormattedMessage.FromMarkup($"[bold]{itemName}[/bold]");
+ var label = new RichTextLabel();
+ label.SetMessage(labelMessage);
+ hBox.AddChild(label);
}
else
{
- hBox.AddChild(new Label
- {
- Text = "???",
- HorizontalExpand = true,
- });
+ var label = new RichTextLabel();
+ label.SetMessage(FormattedMessage.FromMarkup("[bold]???[/bold]"));
+ hBox.AddChild(label);
}
panel.Measure(Vector2Helpers.Infinity);
diff --git a/Content.Client/Fluids/PuddleSystem.cs b/Content.Client/Fluids/PuddleSystem.cs
index 54b1d5b86b9..5dbffe0fd2f 100644
--- a/Content.Client/Fluids/PuddleSystem.cs
+++ b/Content.Client/Fluids/PuddleSystem.cs
@@ -1,7 +1,9 @@
using Content.Client.IconSmoothing;
+using Content.Shared.Chemistry.Components;
using Content.Shared.Fluids;
using Content.Shared.Fluids.Components;
using Robust.Client.GameObjects;
+using Robust.Shared.Map;
namespace Content.Client.Fluids;
@@ -21,7 +23,7 @@ private void OnPuddleAppearance(EntityUid uid, PuddleComponent component, ref Ap
if (args.Sprite == null)
return;
- float volume = 1f;
+ var volume = 1f;
if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj))
{
@@ -64,4 +66,38 @@ private void OnPuddleAppearance(EntityUid uid, PuddleComponent component, ref Ap
args.Sprite.Color *= baseColor;
}
}
+
+ #region Spill
+
+ // Maybe someday we'll have clientside prediction for entity spawning, but not today.
+ // Until then, these methods do nothing on the client.
+ ///
+ public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
+ {
+ puddleUid = EntityUid.Invalid;
+ return false;
+ }
+
+ ///
+ public override bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true)
+ {
+ puddleUid = EntityUid.Invalid;
+ return false;
+ }
+
+ ///
+ public override bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, TransformComponent? transformComponent = null)
+ {
+ puddleUid = EntityUid.Invalid;
+ return false;
+ }
+
+ ///
+ public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, bool tileReact = true)
+ {
+ puddleUid = EntityUid.Invalid;
+ return false;
+ }
+
+ #endregion Spill
}
diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs
index bdbd69d1086..6236cd8e958 100644
--- a/Content.Client/Gameplay/GameplayStateBase.cs
+++ b/Content.Client/Gameplay/GameplayStateBase.cs
@@ -104,7 +104,7 @@ private bool HandleInspect(ICommonSession? session, EntityCoordinates coords, En
public IEnumerable GetClickableEntities(EntityCoordinates coordinates)
{
- return GetClickableEntities(coordinates.ToMap(_entityManager));
+ return GetClickableEntities(coordinates.ToMap(_entityManager, _entitySystemManager.GetEntitySystem()));
}
public IEnumerable GetClickableEntities(MapCoordinates coordinates)
diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs
index 43333439f08..0e0b9dd384d 100644
--- a/Content.Client/Humanoid/MarkingPicker.xaml.cs
+++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs
@@ -124,17 +124,16 @@ public MarkingPicker()
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- SetupCategoryButtons();
CMarkingCategoryButton.OnItemSelected += OnCategoryChange;
CMarkingsUnused.OnItemSelected += item =>
_selectedUnusedMarking = CMarkingsUnused[item.ItemIndex];
- CMarkingAdd.OnPressed += args =>
+ CMarkingAdd.OnPressed += _ =>
MarkingAdd();
CMarkingsUsed.OnItemSelected += OnUsedMarkingSelected;
- CMarkingRemove.OnPressed += args =>
+ CMarkingRemove.OnPressed += _ =>
MarkingRemove();
CMarkingRankUp.OnPressed += _ => SwapMarkingUp();
@@ -146,16 +145,34 @@ public MarkingPicker()
private void SetupCategoryButtons()
{
CMarkingCategoryButton.Clear();
+
+ var validCategories = new List();
for (var i = 0; i < _markingCategories.Count; i++)
{
- if (_ignoreCategories.Contains(_markingCategories[i]))
+ var category = _markingCategories[i];
+ var markings = GetMarkings(category);
+ if (_ignoreCategories.Contains(category) ||
+ markings.Count == 0)
{
continue;
}
- CMarkingCategoryButton.AddItem(Loc.GetString($"markings-category-{_markingCategories[i].ToString()}"), i);
+ validCategories.Add(category);
+ CMarkingCategoryButton.AddItem(Loc.GetString($"markings-category-{category.ToString()}"), i);
+ }
+
+ if (validCategories.Contains(_selectedMarkingCategory))
+ {
+ CMarkingCategoryButton.SelectId(_markingCategories.IndexOf(_selectedMarkingCategory));
+ }
+ else if (validCategories.Count > 0)
+ {
+ _selectedMarkingCategory = validCategories[0];
+ }
+ else
+ {
+ _selectedMarkingCategory = MarkingCategories.Chest;
}
- CMarkingCategoryButton.SelectId(_markingCategories.IndexOf(_selectedMarkingCategory));
}
private string GetMarkingName(MarkingPrototype marking) => Loc.GetString($"marking-{marking.ID}");
@@ -179,16 +196,21 @@ private List GetMarkingStateNames(MarkingPrototype marking)
return result;
}
+ private IReadOnlyDictionary GetMarkings(MarkingCategories category)
+ {
+ return IgnoreSpecies
+ ? _markingManager.MarkingsByCategoryAndSex(category, _currentSex)
+ : _markingManager.MarkingsByCategoryAndSpeciesAndSex(category, _currentSpecies, _currentSex);
+ }
+
public void Populate(string filter)
{
+ SetupCategoryButtons();
+
CMarkingsUnused.Clear();
_selectedUnusedMarking = null;
- var markings = IgnoreSpecies
- ? _markingManager.MarkingsByCategoryAndSex(_selectedMarkingCategory, _currentSex)
- : _markingManager.MarkingsByCategoryAndSpeciesAndSex(_selectedMarkingCategory, _currentSpecies, _currentSex);
-
- var sortedMarkings = markings.Values.Where(m =>
+ var sortedMarkings = GetMarkings(_selectedMarkingCategory).Values.Where(m =>
m.ID.ToLower().Contains(filter.ToLower()) ||
GetMarkingName(m).ToLower().Contains(filter.ToLower())
).OrderBy(p => Loc.GetString(GetMarkingName(p)));
diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs
index 0ab33c65202..4b025608465 100644
--- a/Content.Client/IconSmoothing/IconSmoothSystem.cs
+++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs
@@ -16,8 +16,6 @@ namespace Content.Client.IconSmoothing
[UsedImplicitly]
public sealed partial class IconSmoothSystem : EntitySystem
{
- [Dependency] private readonly IMapManager _mapManager = default!;
-
private readonly Queue _dirtyEntities = new();
private readonly Queue _anchorChangedEntities = new();
@@ -47,7 +45,7 @@ private void OnStartup(EntityUid uid, IconSmoothComponent component, ComponentSt
var xform = Transform(uid);
if (xform.Anchored)
{
- component.LastPosition = _mapManager.TryGetGrid(xform.GridUid, out var grid)
+ component.LastPosition = TryComp(xform.GridUid, out var grid)
? (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates))
: (null, new Vector2i(0, 0));
@@ -134,7 +132,7 @@ public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, Tra
Vector2i pos;
- if (transform.Anchored && _mapManager.TryGetGrid(transform.GridUid, out var grid))
+ if (transform.Anchored && TryComp(transform.GridUid, out var grid))
{
pos = grid.CoordinatesToTile(transform.Coordinates);
}
@@ -144,7 +142,7 @@ public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, Tra
if (comp.LastPosition is not (EntityUid gridId, Vector2i oldPos))
return;
- if (!_mapManager.TryGetGrid(gridId, out grid))
+ if (!TryComp(gridId, out grid))
return;
pos = oldPos;
@@ -206,7 +204,7 @@ private void CalculateNewSprite(EntityUid uid,
{
var directions = DirectionFlag.None;
- if (_mapManager.TryGetGrid(xform.GridUid, out grid))
+ if (TryComp(xform.GridUid, out grid))
{
var pos = grid.TileIndicesFor(xform.Coordinates);
@@ -240,7 +238,7 @@ private void CalculateNewSprite(EntityUid uid,
if (xform.Anchored)
{
- if (!_mapManager.TryGetGrid(xform.GridUid, out grid))
+ if (!TryComp(xform.GridUid, out grid))
{
Log.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridUid} was missing.");
return;
diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs
index 03f4f3f38b7..2e888b3df98 100644
--- a/Content.Client/Input/ContentContexts.cs
+++ b/Content.Client/Input/ContentContexts.cs
@@ -32,6 +32,7 @@ public static void SetupContexts(IInputContextContainer contexts)
common.AddFunction(ContentKeyFunctions.ToggleFullscreen);
common.AddFunction(ContentKeyFunctions.MoveStoredItem);
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
+ common.AddFunction(ContentKeyFunctions.SaveItemLocation);
common.AddFunction(ContentKeyFunctions.Point);
common.AddFunction(ContentKeyFunctions.ZoomOut);
common.AddFunction(ContentKeyFunctions.ZoomIn);
@@ -44,6 +45,9 @@ public static void SetupContexts(IInputContextContainer contexts)
// Not in engine because the engine doesn't understand what a flipped object is
common.AddFunction(ContentKeyFunctions.EditorFlipObject);
+ // Not in engine so that the RCD can rotate objects
+ common.AddFunction(EngineKeyFunctions.EditorRotateObject);
+
var human = contexts.GetContext("human");
human.AddFunction(EngineKeyFunctions.MoveUp);
human.AddFunction(EngineKeyFunctions.MoveDown);
diff --git a/Content.Client/Items/Systems/ItemSystem.cs b/Content.Client/Items/Systems/ItemSystem.cs
index e406ba2b557..5e60d06d0ce 100644
--- a/Content.Client/Items/Systems/ItemSystem.cs
+++ b/Content.Client/Items/Systems/ItemSystem.cs
@@ -93,7 +93,7 @@ private bool TryGetDefaultVisuals(EntityUid uid, ItemComponent item, string defa
else if (TryComp(uid, out SpriteComponent? sprite))
rsi = sprite.BaseRSI;
- if (rsi == null || rsi.Path == null)
+ if (rsi == null)
return false;
var state = (item.HeldPrefix == null)
diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml b/Content.Client/Kitchen/UI/GrinderMenu.xaml
index b83128d004a..dacddd0df68 100644
--- a/Content.Client/Kitchen/UI/GrinderMenu.xaml
+++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml
@@ -3,10 +3,12 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc grinder-menu-title}" MinSize="768 256">
-
-
-
-
+
+
+
+
+
+
diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
index 6e4b7a76180..f97d8a73302 100644
--- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
+++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
@@ -24,6 +24,7 @@ public GrinderMenu(ReagentGrinderBoundUserInterface owner, IEntityManager entity
_entityManager = entityManager;
_prototypeManager = prototypeManager;
_owner = owner;
+ AutoModeButton.OnPressed += owner.ToggleAutoMode;
GrindButton.OnPressed += owner.StartGrinding;
JuiceButton.OnPressed += owner.StartJuicing;
ChamberContentBox.EjectButton.OnPressed += owner.EjectAll;
@@ -56,6 +57,19 @@ public void UpdateState(ReagentGrinderInterfaceState state)
GrindButton.Disabled = !state.CanGrind || !state.Powered;
JuiceButton.Disabled = !state.CanJuice || !state.Powered;
+ switch (state.AutoMode)
+ {
+ case GrinderAutoMode.Grind:
+ AutoModeButton.Text = Loc.GetString("grinder-menu-grind-button");
+ break;
+ case GrinderAutoMode.Juice:
+ AutoModeButton.Text = Loc.GetString("grinder-menu-juice-button");
+ break;
+ default:
+ AutoModeButton.Text = Loc.GetString("grinder-menu-auto-button-off");
+ break;
+ }
+
// TODO move this to a component state and ensure the net ids.
RefreshContentsDisplay(state.ReagentQuantities, _entityManager.GetEntityArray(state.ChamberContents), state.HasBeakerIn);
}
diff --git a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
index 39b85c261b3..e6f108b3050 100644
--- a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
+++ b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
@@ -52,6 +52,11 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
_menu?.HandleMessage(message);
}
+ public void ToggleAutoMode(BaseButton.ButtonEventArgs args)
+ {
+ SendMessage(new ReagentGrinderToggleAutoModeMessage());
+ }
+
public void StartGrinding(BaseButton.ButtonEventArgs? _ = null)
{
SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind));
diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs
index b99d30015ef..ba9351d6746 100644
--- a/Content.Client/LateJoin/LateJoinGui.cs
+++ b/Content.Client/LateJoin/LateJoinGui.cs
@@ -33,7 +33,7 @@ public sealed class LateJoinGui : DefaultWindow
private readonly SpriteSystem _sprites;
private readonly CrewManifestSystem _crewManifest;
- private readonly Dictionary> _jobButtons = new();
+ private readonly Dictionary>> _jobButtons = new();
private readonly Dictionary> _jobCategories = new();
private readonly List _jobLists = new();
@@ -139,7 +139,7 @@ private void RebuildUI()
var jobListScroll = new ScrollContainer()
{
VerticalExpand = true,
- Children = {jobList},
+ Children = { jobList },
Visible = false,
};
@@ -163,11 +163,12 @@ private void RebuildUI()
var departments = _prototypeManager.EnumeratePrototypes().ToArray();
Array.Sort(departments, DepartmentUIComparer.Instance);
+ _jobButtons[id] = new Dictionary>();
+
foreach (var department in departments)
{
var departmentName = Loc.GetString($"department-{department.ID}");
_jobCategories[id] = new Dictionary();
- _jobButtons[id] = new Dictionary();
var stationAvailable = _gameTicker.JobsAvailable[id];
var jobsAvailable = new List();
@@ -223,7 +224,13 @@ private void RebuildUI()
foreach (var prototype in jobsAvailable)
{
var value = stationAvailable[prototype.ID];
- var jobButton = new JobButton(prototype.ID, value);
+
+ var jobLabel = new Label
+ {
+ Margin = new Thickness(5f, 0, 0, 0)
+ };
+
+ var jobButton = new JobButton(jobLabel, prototype.ID, prototype.LocalizedName, value);
var jobSelector = new BoxContainer
{
@@ -241,14 +248,6 @@ private void RebuildUI()
icon.Texture = _sprites.Frame0(jobIcon.Icon);
jobSelector.AddChild(icon);
- var jobLabel = new Label
- {
- Margin = new Thickness(5f, 0, 0, 0),
- Text = value != null ?
- Loc.GetString("late-join-gui-job-slot-capped", ("jobName", prototype.LocalizedName), ("amount", value)) :
- Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", prototype.LocalizedName)),
- };
-
jobSelector.AddChild(jobLabel);
jobButton.AddChild(jobSelector);
category.AddChild(jobButton);
@@ -280,15 +279,43 @@ private void RebuildUI()
jobButton.Disabled = true;
}
- _jobButtons[id][prototype.ID] = jobButton;
+ if (!_jobButtons[id].ContainsKey(prototype.ID))
+ {
+ _jobButtons[id][prototype.ID] = new List();
+ }
+
+ _jobButtons[id][prototype.ID].Add(jobButton);
}
}
}
}
- private void JobsAvailableUpdated(IReadOnlyDictionary> _)
+ private void JobsAvailableUpdated(IReadOnlyDictionary> updatedJobs)
{
- RebuildUI();
+ foreach (var stationEntries in updatedJobs)
+ {
+ if (_jobButtons.ContainsKey(stationEntries.Key))
+ {
+ var jobsAvailable = stationEntries.Value;
+
+ var existingJobEntries = _jobButtons[stationEntries.Key];
+ foreach (var existingJobEntry in existingJobEntries)
+ {
+ if (jobsAvailable.ContainsKey(existingJobEntry.Key))
+ {
+ var updatedJobValue = jobsAvailable[existingJobEntry.Key];
+ foreach (var matchingJobButton in existingJobEntry.Value)
+ {
+ if (matchingJobButton.Amount != updatedJobValue)
+ {
+ matchingJobButton.RefreshLabel(updatedJobValue);
+ matchingJobButton.Disabled |= matchingJobButton.Amount == 0;
+ }
+ }
+ }
+ }
+ }
+ }
}
protected override void Dispose(bool disposing)
@@ -307,14 +334,33 @@ protected override void Dispose(bool disposing)
sealed class JobButton : ContainerButton
{
+ public Label JobLabel { get; }
public string JobId { get; }
- public uint? Amount { get; }
+ public string JobLocalisedName { get; }
+ public uint? Amount { get; private set; }
+ private bool _initialised = false;
- public JobButton(string jobId, uint? amount)
+ public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount)
{
+ JobLabel = jobLabel;
JobId = jobId;
- Amount = amount;
+ JobLocalisedName = jobLocalisedName;
+ RefreshLabel(amount);
AddStyleClass(StyleClassButton);
+ _initialised = true;
+ }
+
+ public void RefreshLabel(uint? amount)
+ {
+ if (Amount == amount && _initialised)
+ {
+ return;
+ }
+ Amount = amount;
+
+ JobLabel.Text = Amount != null ?
+ Loc.GetString("late-join-gui-job-slot-capped", ("jobName", JobLocalisedName), ("amount", Amount)) :
+ Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", JobLocalisedName));
}
}
}
diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml
index 2b97166f05a..6f484d8c7be 100644
--- a/Content.Client/Lathe/UI/LatheMenu.xaml
+++ b/Content.Client/Lathe/UI/LatheMenu.xaml
@@ -124,9 +124,7 @@
+ Orientation="Vertical">
-
-
+
+
+
+
diff --git a/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs b/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs
index 3ef247d5297..31d99624a8a 100644
--- a/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs
+++ b/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs
@@ -11,7 +11,7 @@ namespace Content.Client.Materials.UI;
/// This widget is one row in the lathe eject menu.
///
[GenerateTypedNameReferences]
-public sealed partial class MaterialStorageControl : BoxContainer
+public sealed partial class MaterialStorageControl : ScrollContainer
{
[Dependency] private readonly IEntityManager _entityManager = default!;
@@ -63,7 +63,7 @@ protected override void FrameUpdate(FrameEventArgs args)
}
var children = new List();
- children.AddRange(Children.OfType());
+ children.AddRange(MaterialList.Children.OfType());
foreach (var display in children)
{
@@ -71,7 +71,7 @@ protected override void FrameUpdate(FrameEventArgs args)
if (extra.Contains(mat))
{
- RemoveChild(display);
+ MaterialList.RemoveChild(display);
continue;
}
@@ -83,7 +83,7 @@ protected override void FrameUpdate(FrameEventArgs args)
foreach (var mat in missing)
{
var volume = mats[mat];
- AddChild(new MaterialDisplay(_owner.Value, mat, volume, canEject));
+ MaterialList.AddChild(new MaterialDisplay(_owner.Value, mat, volume, canEject));
}
_currentMaterials = mats;
diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
index 39326c8a99c..863412e5532 100644
--- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
+++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
@@ -210,9 +210,9 @@ private void PopulateDepartmentList(IEnumerable departmentSens
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead");
}
- else if (sensor.TotalDamage != null)
+ else if (sensor.DamagePercentage != null)
{
- var index = MathF.Round(4f * (sensor.TotalDamage.Value / 100f));
+ var index = MathF.Round(4f * sensor.DamagePercentage.Value);
if (index >= 5)
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs
index f0836ee9b6f..b7f5e48821f 100644
--- a/Content.Client/Movement/Systems/JetpackSystem.cs
+++ b/Content.Client/Movement/Systems/JetpackSystem.cs
@@ -4,6 +4,7 @@
using Content.Shared.Movement.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
@@ -12,7 +13,6 @@ namespace Content.Client.Movement.Systems;
public sealed class JetpackSystem : SharedJetpackSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ClothingSystem _clothing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
@@ -75,7 +75,7 @@ private void CreateParticles(EntityUid uid)
var coordinates = uidXform.Coordinates;
var gridUid = coordinates.GetGridUid(EntityManager);
- if (_mapManager.TryGetGrid(gridUid, out var grid))
+ if (TryComp(gridUid, out var grid))
{
coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform)));
}
diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs
index 548edd601ce..709601a57b6 100644
--- a/Content.Client/NPC/PathfindingSystem.cs
+++ b/Content.Client/NPC/PathfindingSystem.cs
@@ -23,6 +23,7 @@ public sealed class PathfindingSystem : SharedPathfindingSystem
[Dependency] private readonly IResourceCache _cache = default!;
[Dependency] private readonly NPCSteeringSystem _steering = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
public PathfindingDebugMode Modes
{
@@ -39,7 +40,7 @@ public PathfindingDebugMode Modes
}
else if (!overlayManager.HasOverlay())
{
- overlayManager.AddOverlay(new PathfindingOverlay(EntityManager, _eyeManager, _inputManager, _mapManager, _cache, this, _mapSystem));
+ overlayManager.AddOverlay(new PathfindingOverlay(EntityManager, _eyeManager, _inputManager, _mapManager, _cache, this, _mapSystem, _transformSystem));
}
if ((value & PathfindingDebugMode.Steering) != 0x0)
@@ -140,6 +141,7 @@ public sealed class PathfindingOverlay : Overlay
private readonly IMapManager _mapManager;
private readonly PathfindingSystem _system;
private readonly MapSystem _mapSystem;
+ private readonly SharedTransformSystem _transformSystem;
public override OverlaySpace Space => OverlaySpace.ScreenSpace | OverlaySpace.WorldSpace;
@@ -153,7 +155,8 @@ public PathfindingOverlay(
IMapManager mapManager,
IResourceCache cache,
PathfindingSystem system,
- MapSystem mapSystem)
+ MapSystem mapSystem,
+ SharedTransformSystem transformSystem)
{
_entManager = entManager;
_eyeManager = eyeManager;
@@ -161,6 +164,7 @@ public PathfindingOverlay(
_mapManager = mapManager;
_system = system;
_mapSystem = mapSystem;
+ _transformSystem = transformSystem;
_font = new VectorFont(cache.GetResource("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
@@ -285,7 +289,6 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle)
var invGridMatrix = gridXform.InvWorldMatrix;
DebugPathPoly? nearest = null;
- var nearestDistance = float.MaxValue;
foreach (var poly in tile)
{
@@ -480,7 +483,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
if (neighborPoly.NetEntity != poly.GraphUid)
{
color = Color.Green;
- var neighborMap = _entManager.GetCoordinates(neighborPoly).ToMap(_entManager);
+ var neighborMap = _entManager.GetCoordinates(neighborPoly).ToMap(_entManager, _transformSystem);
if (neighborMap.MapId != args.MapId)
continue;
diff --git a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
index f10eb9ed8b1..691bcb41dbd 100644
--- a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
+++ b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
@@ -80,7 +80,7 @@ private void DrawScreen(in OverlayDrawArgs args)
var xform = _entityManager.GetComponent(_entityManager.GetEntity(node.Entity));
- if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
+ if (!_entityManager.TryGetComponent(xform.GridUid, out var grid))
return;
var gridTile = grid.TileIndicesFor(xform.Coordinates);
@@ -145,7 +145,7 @@ private void DrawWorld(in OverlayDrawArgs overlayDrawArgs)
foreach (var (gridId, gridDict) in _gridIndex)
{
- var grid = _mapManager.GetGrid(gridId);
+ var grid = _entityManager.GetComponent(gridId);
var (_, _, worldMatrix, invMatrix) = _entityManager.GetComponent(gridId).GetWorldPositionRotationMatrixWithInv();
var lCursorBox = invMatrix.TransformBox(cursorBox);
diff --git a/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs b/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs
deleted file mode 100644
index f8c3f7c447f..00000000000
--- a/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using Content.Shared.Nutrition.EntitySystems;
-
-namespace Content.Client.Nutrition.EntitySystems;
-
-public sealed class OpenableSystem : SharedOpenableSystem
-{
-}
diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
index ce5cf421aef..aca9efcfe26 100644
--- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
@@ -183,6 +183,7 @@ void AddCheckBox(string checkBoxName, bool currentState, Action
+
+
+
+
+
+
@@ -65,6 +74,3 @@
-
-
-
diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
index 3b9c41efdfb..0eff811fa49 100644
--- a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
@@ -3,11 +3,14 @@
using Content.Shared.CCVar;
using Content.Shared.HUD;
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;
@@ -16,6 +19,7 @@ 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!;
@@ -55,8 +59,11 @@ public MiscTab()
UpdateApplyButton();
};
+ ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel.UserData.PatronTier is { } patron;
+
HudThemeOption.OnItemSelected += OnHudThemeChanged;
DiscordRich.OnToggled += OnCheckBoxToggled;
+ ShowOocPatronColor.OnToggled += OnCheckBoxToggled;
ShowLoocAboveHeadCheckBox.OnToggled += OnCheckBoxToggled;
ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled;
ShowCombatModeIndicatorsCheckBox.OnToggled += OnCheckBoxToggled;
@@ -66,12 +73,14 @@ public MiscTab()
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);
@@ -81,6 +90,7 @@ public MiscTab()
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);
@@ -101,6 +111,13 @@ private void OnHudThemeChanged(OptionButton.ItemSelectedEventArgs args)
UpdateApplyButton();
}
+ private void OnChatWindowOpacitySliderChanged(Range range)
+ {
+ ChatWindowOpacityLabel.Text = Loc.GetString("ui-options-chat-window-opacity-percent",
+ ("opacity", range.Value));
+ UpdateApplyButton();
+ }
+
private void OnScreenShakeIntensitySliderChanged(Range obj)
{
ScreenShakeIntensityLabel.Text = Loc.GetString("ui-options-screen-shake-percent", ("intensity", ScreenShakeIntensitySlider.Value / 100f));
@@ -121,12 +138,14 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
_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);
@@ -148,12 +167,14 @@ private void UpdateApplyButton()
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);
@@ -164,12 +185,14 @@ private void UpdateApplyButton()
isShowHeldItemSame &&
isCombatModeIndicatorsSame &&
isOpaqueStorageWindow &&
+ isOocPatronColorShowSame &&
isLoocShowSame &&
isFancyChatSame &&
isFancyBackgroundSame &&
isEnableColorNameSame &&
isColorblindFriendly &&
isReducedMotionSame &&
+ isChatWindowOpacitySame &&
isScreenShakeIntensitySame &&
// isToggleWalkSame &&
isStaticStorageUISame;
diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs
index 9e562b5dd37..c1c0ae93ec1 100644
--- a/Content.Client/Overlays/EntityHealthBarOverlay.cs
+++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs
@@ -19,7 +19,6 @@ namespace Content.Client.Overlays;
///
public sealed class EntityHealthBarOverlay : Overlay
{
- [Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly IEntityManager _entManager;
private readonly SharedTransformSystem _transform;
private readonly MobStateSystem _mobStateSystem;
@@ -27,17 +26,14 @@ public sealed class EntityHealthBarOverlay : Overlay
private readonly ProgressColorSystem _progressColor;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
public HashSet DamageContainers = new();
- private readonly ShaderInstance _shader;
public EntityHealthBarOverlay(IEntityManager entManager)
{
- IoCManager.InjectDependencies(this);
_entManager = entManager;
_transform = _entManager.System();
_mobStateSystem = _entManager.System();
_mobThresholdSystem = _entManager.System();
_progressColor = _entManager.System();
- _shader = _prototype.Index("unshaded").Instance();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -50,8 +46,6 @@ protected override void Draw(in OverlayDrawArgs args)
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3.CreateRotation(-rotation);
- handle.UseShader(_shader);
-
var query = _entManager.AllEntityQueryEnumerator();
while (query.MoveNext(out var uid,
out var mobThresholdsComponent,
@@ -122,7 +116,6 @@ protected override void Draw(in OverlayDrawArgs args)
handle.DrawRect(pixelDarken, Black.WithAlpha(128));
}
- handle.UseShader(null);
handle.SetTransform(Matrix3.Identity);
}
diff --git a/Content.Client/Overlays/ShowHealthIconsSystem.cs b/Content.Client/Overlays/ShowHealthIconsSystem.cs
index 6ed9d6a41db..a546cf4d828 100644
--- a/Content.Client/Overlays/ShowHealthIconsSystem.cs
+++ b/Content.Client/Overlays/ShowHealthIconsSystem.cs
@@ -1,5 +1,7 @@
+using Content.Shared.Atmos.Rotting;
using Content.Shared.Damage;
using Content.Shared.Inventory.Events;
+using Content.Shared.Mobs.Components;
using Content.Shared.Overlays;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
@@ -17,9 +19,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem DamageContainers = new();
- [ValidatePrototypeId]
- private const string HealthIconFine = "HealthIconFine";
-
public override void Initialize()
{
base.Initialize();
@@ -45,18 +44,20 @@ protected override void DeactivateInternal()
DamageContainers.Clear();
}
- private void OnGetStatusIconsEvent(EntityUid uid, DamageableComponent damageableComponent, ref GetStatusIconsEvent args)
+ private void OnGetStatusIconsEvent(Entity entity, ref GetStatusIconsEvent args)
{
if (!IsActive || args.InContainer)
return;
- var healthIcons = DecideHealthIcons(damageableComponent);
+ var healthIcons = DecideHealthIcons(entity);
args.StatusIcons.AddRange(healthIcons);
}
- private IReadOnlyList DecideHealthIcons(DamageableComponent damageableComponent)
+ private IReadOnlyList DecideHealthIcons(Entity entity)
{
+ var damageableComponent = entity.Comp;
+
if (damageableComponent.DamageContainerID == null ||
!DamageContainers.Contains(damageableComponent.DamageContainerID))
{
@@ -66,10 +67,16 @@ private IReadOnlyList DecideHealthIcons(DamageableComponent
var result = new List();
// Here you could check health status, diseases, mind status, etc. and pick a good icon, or multiple depending on whatever.
- if (damageableComponent?.DamageContainerID == "Biological" &&
- _prototypeMan.TryIndex(HealthIconFine, out var healthyIcon))
+ if (damageableComponent?.DamageContainerID == "Biological")
{
- result.Add(healthyIcon);
+ if (TryComp(entity, out var state))
+ {
+ // Since there is no MobState for a rotting mob, we have to deal with this case first.
+ if (HasComp(entity) && _prototypeMan.TryIndex(damageableComponent.RottingIcon, out var rottingIcon))
+ result.Add(rottingIcon);
+ else if (damageableComponent.HealthIcons.TryGetValue(state.CurrentState, out var value) && _prototypeMan.TryIndex(value, out var icon))
+ result.Add(icon);
+ }
}
return result;
diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs
index 7a8a6e39cff..31042854d4a 100644
--- a/Content.Client/Physics/Controllers/MoverController.cs
+++ b/Content.Client/Physics/Controllers/MoverController.cs
@@ -1,6 +1,7 @@
using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems;
-using Content.Shared.Pulling.Components;
+using Robust.Client.GameObjects;
using Robust.Client.Physics;
using Robust.Client.Player;
using Robust.Shared.Physics.Components;
@@ -24,7 +25,7 @@ public override void Initialize()
SubscribeLocalEvent(OnUpdatePredicted);
SubscribeLocalEvent(OnUpdateRelayTargetPredicted);
- SubscribeLocalEvent(OnUpdatePullablePredicted);
+ SubscribeLocalEvent(OnUpdatePullablePredicted);
}
private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args)
@@ -40,7 +41,7 @@ private void OnUpdateRelayTargetPredicted(EntityUid uid, MovementRelayTargetComp
args.IsPredicted = true;
}
- private void OnUpdatePullablePredicted(EntityUid uid, SharedPullableComponent component, ref UpdateIsPredictedEvent args)
+ private void OnUpdatePullablePredicted(EntityUid uid, PullableComponent component, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is being pulled by the player.
// Disable prediction if an entity is being pulled by some non-player entity.
diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs
index a8ec7b37a0b..677092e1918 100644
--- a/Content.Client/Pinpointer/UI/NavMapControl.cs
+++ b/Content.Client/Pinpointer/UI/NavMapControl.cs
@@ -114,9 +114,16 @@ public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDispl
VerticalExpand = false,
Children =
{
- _zoom,
- _beacons,
- _recenter,
+ new BoxContainer()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Horizontal,
+ Children =
+ {
+ _zoom,
+ _beacons,
+ _recenter
+ }
+ }
}
};
diff --git a/Content.Client/Popups/PopupOverlay.cs b/Content.Client/Popups/PopupOverlay.cs
index 3bfa7278734..fb6bb3bf565 100644
--- a/Content.Client/Popups/PopupOverlay.cs
+++ b/Content.Client/Popups/PopupOverlay.cs
@@ -22,7 +22,7 @@ public sealed class PopupOverlay : Overlay
private readonly PopupSystem _popup;
private readonly PopupUIController _controller;
private readonly ExamineSystemShared _examine;
-
+ private readonly SharedTransformSystem _transform;
private readonly ShaderInstance _shader;
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
@@ -35,6 +35,7 @@ public PopupOverlay(
IUserInterfaceManager uiManager,
PopupUIController controller,
ExamineSystemShared examine,
+ SharedTransformSystem transform,
PopupSystem popup)
{
_configManager = configManager;
@@ -42,6 +43,7 @@ public PopupOverlay(
_playerMgr = playerMgr;
_uiManager = uiManager;
_examine = examine;
+ _transform = transform;
_popup = popup;
_controller = controller;
@@ -76,7 +78,7 @@ private void DrawWorld(DrawingHandleScreen worldHandle, OverlayDrawArgs args, fl
foreach (var popup in _popup.WorldLabels)
{
- var mapPos = popup.InitialPos.ToMap(_entManager);
+ var mapPos = popup.InitialPos.ToMap(_entManager, _transform);
if (mapPos.MapId != args.MapId)
continue;
diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs
index 46c1e0cdf62..fcc8bfc420a 100644
--- a/Content.Client/Popups/PopupSystem.cs
+++ b/Content.Client/Popups/PopupSystem.cs
@@ -28,6 +28,7 @@ public sealed class PopupSystem : SharedPopupSystem
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
public IReadOnlyList WorldLabels => _aliveWorldLabels;
public IReadOnlyList CursorLabels => _aliveCursorLabels;
@@ -54,6 +55,7 @@ public override void Initialize()
_uiManager,
_uiManager.GetUIController(),
_examine,
+ _transform,
this));
}
@@ -161,10 +163,13 @@ public override void PopupEntity(string? message, EntityUid uid, Filter filter,
PopupEntity(message, uid, type);
}
- public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
+ public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
{
+ if (recipient == null)
+ return;
+
if (_timing.IsFirstTimePredicted)
- PopupEntity(message, uid, recipient, type);
+ PopupEntity(message, uid, recipient.Value, type);
}
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
diff --git a/Content.Client/Pulling/PullingSystem.cs b/Content.Client/Pulling/PullingSystem.cs
deleted file mode 100644
index 556dadd00da..00000000000
--- a/Content.Client/Pulling/PullingSystem.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
-using JetBrains.Annotations;
-using Robust.Client.Physics;
-
-namespace Content.Client.Pulling
-{
- [UsedImplicitly]
- public sealed class PullingSystem : SharedPullingSystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- UpdatesAfter.Add(typeof(PhysicsSystem));
-
- SubscribeLocalEvent(OnPullableMove);
- SubscribeLocalEvent(OnPullableStopMove);
- }
- }
-}
diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs
new file mode 100644
index 00000000000..da7b22c91a8
--- /dev/null
+++ b/Content.Client/RCD/AlignRCDConstruction.cs
@@ -0,0 +1,122 @@
+using System.Numerics;
+using Content.Client.Gameplay;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction;
+using Content.Shared.RCD.Components;
+using Content.Shared.RCD.Systems;
+using Robust.Client.Placement;
+using Robust.Client.Player;
+using Robust.Client.State;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+
+namespace Content.Client.RCD;
+
+public sealed class AlignRCDConstruction : PlacementMode
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!;
+ [Dependency] private readonly RCDSystem _rcdSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IStateManager _stateManager = default!;
+
+ private const float SearchBoxSize = 2f;
+ private const float PlaceColorBaseAlpha = 0.5f;
+
+ private EntityCoordinates _unalignedMouseCoords = default;
+
+ ///
+ /// This placement mode is not on the engine because it is content specific (i.e., for the RCD)
+ ///
+ public AlignRCDConstruction(PlacementManager pMan) : base(pMan)
+ {
+ var dependencies = IoCManager.Instance!;
+ _entityManager = dependencies.Resolve();
+ _mapManager = dependencies.Resolve();
+ _playerManager = dependencies.Resolve();
+ _stateManager = dependencies.Resolve();
+
+ _mapSystem = _entityManager.System();
+ _rcdSystem = _entityManager.System();
+ _transformSystem = _entityManager.System();
+
+ ValidPlaceColor = ValidPlaceColor.WithAlpha(PlaceColorBaseAlpha);
+ }
+
+ public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
+ {
+ _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen);
+ MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager);
+
+ var gridId = MouseCoords.GetGridUid(_entityManager);
+
+ if (!_entityManager.TryGetComponent(gridId, out var mapGrid))
+ return;
+
+ CurrentTile = _mapSystem.GetTileRef(gridId.Value, mapGrid, MouseCoords);
+
+ float tileSize = mapGrid.TileSize;
+ GridDistancing = tileSize;
+
+ if (pManager.CurrentPermission!.IsTile)
+ {
+ MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2,
+ CurrentTile.Y + tileSize / 2));
+ }
+ else
+ {
+ MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2 + pManager.PlacementOffset.X,
+ CurrentTile.Y + tileSize / 2 + pManager.PlacementOffset.Y));
+ }
+ }
+
+ public override bool IsValidPosition(EntityCoordinates position)
+ {
+ var player = _playerManager.LocalSession?.AttachedEntity;
+
+ // If the destination is out of interaction range, set the placer alpha to zero
+ if (!_entityManager.TryGetComponent(player, out var xform))
+ return false;
+
+ if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange))
+ {
+ InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0);
+ return false;
+ }
+
+ // Otherwise restore the alpha value
+ else
+ {
+ InvalidPlaceColor = InvalidPlaceColor.WithAlpha(PlaceColorBaseAlpha);
+ }
+
+ // Determine if player is carrying an RCD in their active hand
+ if (!_entityManager.TryGetComponent(player, out var hands))
+ return false;
+
+ var heldEntity = hands.ActiveHand?.HeldEntity;
+
+ if (!_entityManager.TryGetComponent(heldEntity, out var rcd))
+ return false;
+
+ // Retrieve the map grid data for the position
+ if (!_rcdSystem.TryGetMapGridData(position, out var mapGridData))
+ return false;
+
+ // Determine if the user is hovering over a target
+ var currentState = _stateManager.CurrentState;
+
+ if (currentState is not GameplayStateBase screen)
+ return false;
+
+ var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem));
+
+ // Determine if the RCD operation is valid or not
+ if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false))
+ return false;
+
+ return true;
+ }
+}
diff --git a/Content.Client/RCD/RCDConstructionGhostSystem.cs b/Content.Client/RCD/RCDConstructionGhostSystem.cs
new file mode 100644
index 00000000000..792916b8922
--- /dev/null
+++ b/Content.Client/RCD/RCDConstructionGhostSystem.cs
@@ -0,0 +1,78 @@
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction;
+using Content.Shared.RCD;
+using Content.Shared.RCD.Components;
+using Content.Shared.RCD.Systems;
+using Robust.Client.Placement;
+using Robust.Client.Player;
+using Robust.Shared.Enums;
+
+namespace Content.Client.RCD;
+
+public sealed class RCDConstructionGhostSystem : EntitySystem
+{
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly RCDSystem _rcdSystem = default!;
+ [Dependency] private readonly IPlacementManager _placementManager = default!;
+
+ private string _placementMode = typeof(AlignRCDConstruction).Name;
+ private Direction _placementDirection = default;
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ // Get current placer data
+ var placerEntity = _placementManager.CurrentPermission?.MobUid;
+ var placerProto = _placementManager.CurrentPermission?.EntityType;
+ var placerIsRCD = HasComp(placerEntity);
+
+ // Exit if erasing or the current placer is not an RCD (build mode is active)
+ if (_placementManager.Eraser || (placerEntity != null && !placerIsRCD))
+ return;
+
+ // Determine if player is carrying an RCD in their active hand
+ var player = _playerManager.LocalSession?.AttachedEntity;
+
+ if (!TryComp(player, out var hands))
+ return;
+
+ var heldEntity = hands.ActiveHand?.HeldEntity;
+
+ if (!TryComp(heldEntity, out var rcd))
+ {
+ // If the player was holding an RCD, but is no longer, cancel placement
+ if (placerIsRCD)
+ _placementManager.Clear();
+
+ return;
+ }
+
+ // Update the direction the RCD prototype based on the placer direction
+ if (_placementDirection != _placementManager.Direction)
+ {
+ _placementDirection = _placementManager.Direction;
+ RaiseNetworkEvent(new RCDConstructionGhostRotationEvent(GetNetEntity(heldEntity.Value), _placementDirection));
+ }
+
+ // If the placer has not changed, exit
+ _rcdSystem.UpdateCachedPrototype(heldEntity.Value, rcd);
+
+ if (heldEntity == placerEntity && rcd.CachedPrototype.Prototype == placerProto)
+ return;
+
+ // Create a new placer
+ var newObjInfo = new PlacementInformation
+ {
+ MobUid = heldEntity.Value,
+ PlacementOption = _placementMode,
+ EntityType = rcd.CachedPrototype.Prototype,
+ Range = (int) Math.Ceiling(SharedInteractionSystem.InteractionRange),
+ IsTile = (rcd.CachedPrototype.Mode == RcdMode.ConstructTile),
+ UseEditorContext = false,
+ };
+
+ _placementManager.Clear();
+ _placementManager.BeginPlacing(newObjInfo);
+ }
+}
diff --git a/Content.Client/RCD/RCDMenu.xaml b/Content.Client/RCD/RCDMenu.xaml
new file mode 100644
index 00000000000..b3d5367a5fd
--- /dev/null
+++ b/Content.Client/RCD/RCDMenu.xaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/RCD/RCDMenu.xaml.cs b/Content.Client/RCD/RCDMenu.xaml.cs
new file mode 100644
index 00000000000..8679e789dc7
--- /dev/null
+++ b/Content.Client/RCD/RCDMenu.xaml.cs
@@ -0,0 +1,137 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.RCD;
+using Content.Shared.RCD.Components;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using System.Numerics;
+
+namespace Content.Client.RCD;
+
+[GenerateTypedNameReferences]
+public sealed partial class RCDMenu : RadialMenu
+{
+ [Dependency] private readonly EntityManager _entManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+
+ private readonly SpriteSystem _spriteSystem;
+
+ public event Action>? SendRCDSystemMessageAction;
+
+ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui)
+ {
+ IoCManager.InjectDependencies(this);
+ RobustXamlLoader.Load(this);
+
+ _spriteSystem = _entManager.System();
+
+ // Find the main radial container
+ var main = FindControl("Main");
+
+ if (main == null)
+ return;
+
+ // Populate secondary radial containers
+ if (!_entManager.TryGetComponent(owner, out var rcd))
+ return;
+
+ foreach (var protoId in rcd.AvailablePrototypes)
+ {
+ if (!_protoManager.TryIndex(protoId, out var proto))
+ continue;
+
+ if (proto.Mode == RcdMode.Invalid)
+ continue;
+
+ var parent = FindControl(proto.Category);
+
+ if (parent == null)
+ continue;
+
+ var name = Loc.GetString(proto.SetName);
+ name = char.ToUpper(name[0]) + name.Remove(0, 1);
+
+ var button = new RCDMenuButton()
+ {
+ StyleClasses = { "RadialMenuButton" },
+ SetSize = new Vector2(64f, 64f),
+ ToolTip = name,
+ ProtoId = protoId,
+ };
+
+ if (proto.Sprite != null)
+ {
+ var tex = new TextureRect()
+ {
+ VerticalAlignment = VAlignment.Center,
+ HorizontalAlignment = HAlignment.Center,
+ Texture = _spriteSystem.Frame0(proto.Sprite),
+ TextureScale = new Vector2(2f, 2f),
+ };
+
+ button.AddChild(tex);
+ }
+
+ parent.AddChild(button);
+
+ // Ensure that the button that transitions the menu to the associated category layer
+ // is visible in the main radial container (as these all start with Visible = false)
+ foreach (var child in main.Children)
+ {
+ var castChild = child as RadialMenuTextureButton;
+
+ if (castChild is not RadialMenuTextureButton)
+ continue;
+
+ if (castChild.TargetLayer == proto.Category)
+ {
+ castChild.Visible = true;
+ break;
+ }
+ }
+ }
+
+ // Set up menu actions
+ foreach (var child in Children)
+ AddRCDMenuButtonOnClickActions(child);
+
+ OnChildAdded += AddRCDMenuButtonOnClickActions;
+
+ SendRCDSystemMessageAction += bui.SendRCDSystemMessage;
+ }
+
+ private void AddRCDMenuButtonOnClickActions(Control control)
+ {
+ var radialContainer = control as RadialContainer;
+
+ if (radialContainer == null)
+ return;
+
+ foreach (var child in radialContainer.Children)
+ {
+ var castChild = child as RCDMenuButton;
+
+ if (castChild == null)
+ continue;
+
+ castChild.OnButtonUp += _ =>
+ {
+ SendRCDSystemMessageAction?.Invoke(castChild.ProtoId);
+ Close();
+ };
+ }
+ }
+}
+
+public sealed class RCDMenuButton : RadialMenuTextureButton
+{
+ public ProtoId ProtoId { get; set; }
+
+ public RCDMenuButton()
+ {
+
+ }
+}
diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs
new file mode 100644
index 00000000000..a37dbcecf8c
--- /dev/null
+++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs
@@ -0,0 +1,49 @@
+using Content.Shared.RCD;
+using Content.Shared.RCD.Components;
+using JetBrains.Annotations;
+using Robust.Client.Graphics;
+using Robust.Client.Input;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.RCD;
+
+[UsedImplicitly]
+public sealed class RCDMenuBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IClyde _displayManager = default!;
+ [Dependency] private readonly IInputManager _inputManager = default!;
+
+ private RCDMenu? _menu;
+
+ public RCDMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ IoCManager.InjectDependencies(this);
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new(Owner, this);
+ _menu.OnClose += Close;
+
+ // Open the menu, centered on the mouse
+ var vpSize = _displayManager.ScreenSize;
+ _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
+ }
+
+ public void SendRCDSystemMessage(ProtoId protoId)
+ {
+ // A predicted message cannot be used here as the RCD UI is closed immediately
+ // after this message is sent, which will stop the server from receiving it
+ SendMessage(new RCDSystemMessage(protoId));
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing) return;
+
+ _menu?.Dispose();
+ }
+}
diff --git a/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs b/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs
index 8c721fa7771..ef6283b6ff4 100644
--- a/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs
+++ b/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs
@@ -4,13 +4,12 @@
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
-using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
namespace Content.Client.Radiation.Overlays;
public sealed class RadiationDebugOverlay : Overlay
{
- [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly RadiationSystem _radiation;
@@ -63,7 +62,7 @@ private void DrawScreenRays(OverlayDrawArgs args)
{
var gridUid = _entityManager.GetEntity(netGrid);
- if (!_mapManager.TryGetGrid(gridUid, out var grid))
+ if (!_entityManager.TryGetComponent(gridUid, out var grid))
continue;
foreach (var (tile, rads) in blockers)
@@ -88,7 +87,7 @@ private void DrawScreenResistance(OverlayDrawArgs args)
{
var gridUid = _entityManager.GetEntity(netGrid);
- if (!_mapManager.TryGetGrid(gridUid, out var grid))
+ if (!_entityManager.TryGetComponent(gridUid, out var grid))
continue;
if (query.TryGetComponent(gridUid, out var trs) && trs.MapID != args.MapId)
continue;
@@ -127,7 +126,7 @@ private void DrawWorld(in OverlayDrawArgs args)
{
var gridUid = _entityManager.GetEntity(netGrid);
- if (!_mapManager.TryGetGrid(gridUid, out var grid))
+ if (!_entityManager.TryGetComponent(gridUid, out var grid))
continue;
var (destTile, _) = blockers.Last();
var destWorld = grid.GridTileToWorldPos(destTile);
diff --git a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
index adc535b2684..9012767ef3f 100644
--- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
+++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
@@ -121,7 +121,10 @@ private void RadiationQuery(IEye? currentEye)
private bool PulseQualifies(EntityUid pulseEntity, MapCoordinates currentEyeLoc)
{
- return _entityManager.GetComponent(pulseEntity).MapID == currentEyeLoc.MapId && _entityManager.GetComponent(pulseEntity).Coordinates.InRange(_entityManager, EntityCoordinates.FromMap(_entityManager, _entityManager.GetComponent(pulseEntity).ParentUid, currentEyeLoc), MaxDist);
+ var transformComponent = _entityManager.GetComponent(pulseEntity);
+ var transformSystem = _entityManager.System();
+ return transformComponent.MapID == currentEyeLoc.MapId
+ && transformComponent.Coordinates.InRange(_entityManager, transformSystem, EntityCoordinates.FromMap(transformComponent.ParentUid, currentEyeLoc, transformSystem, _entityManager), MaxDist);
}
private sealed record RadiationShaderInstance(MapCoordinates CurrentMapCoords, float Range, TimeSpan Start, float Duration)
diff --git a/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs b/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs
new file mode 100644
index 00000000000..d6a9057f08e
--- /dev/null
+++ b/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs
@@ -0,0 +1,16 @@
+using Content.Client.Remote.UI;
+using Content.Client.Items;
+using Content.Shared.Remotes.EntitySystems;
+using Content.Shared.Remotes.Components;
+
+namespace Content.Client.Remotes.EntitySystems;
+
+public sealed class DoorRemoteSystem : SharedDoorRemoteSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ Subs.ItemStatus(ent => new DoorRemoteStatusControl(ent));
+ }
+}
diff --git a/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs b/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs
new file mode 100644
index 00000000000..94589ecdaab
--- /dev/null
+++ b/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs
@@ -0,0 +1,46 @@
+using Content.Client.Message;
+using Content.Client.Stylesheets;
+using Content.Shared.Remotes.Components;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Remote.UI;
+
+public sealed class DoorRemoteStatusControl : Control
+{
+ private readonly Entity _entity;
+ private readonly RichTextLabel _label;
+
+ // set to toggle bolts initially just so that it updates on first pickup of remote
+ private OperatingMode PrevOperatingMode = OperatingMode.placeholderForUiUpdates;
+
+ public DoorRemoteStatusControl(Entity entity)
+ {
+ _entity = entity;
+ _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
+ AddChild(_label);
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+
+ // only updates the UI if any of the details are different than they previously were
+ if (PrevOperatingMode == _entity.Comp.Mode)
+ return;
+
+ PrevOperatingMode = _entity.Comp.Mode;
+
+ // Update current volume and injector state
+ var modeStringLocalized = Loc.GetString(_entity.Comp.Mode switch
+ {
+ OperatingMode.OpenClose => "door-remote-open-close-text",
+ OperatingMode.ToggleBolts => "door-remote-toggle-bolt-text",
+ OperatingMode.ToggleEmergencyAccess => "door-remote-emergency-access-text",
+ _ => "door-remote-invalid-text"
+ });
+
+ _label.SetMarkup(Loc.GetString("door-remote-mode-label", ("modeString", modeStringLocalized)));
+ }
+}
diff --git a/Content.Client/Replay/ContentReplayPlaybackManager.cs b/Content.Client/Replay/ContentReplayPlaybackManager.cs
index bc979575f58..f90731bfa75 100644
--- a/Content.Client/Replay/ContentReplayPlaybackManager.cs
+++ b/Content.Client/Replay/ContentReplayPlaybackManager.cs
@@ -1,8 +1,10 @@
+using System.IO.Compression;
using Content.Client.Administration.Managers;
using Content.Client.Launcher;
using Content.Client.MainMenu;
using Content.Client.Replay.Spectator;
using Content.Client.Replay.UI.Loading;
+using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Chat;
using Content.Shared.Chat;
using Content.Shared.Effects;
@@ -24,7 +26,13 @@
using Robust.Client.State;
using Robust.Client.Timing;
using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared;
+using Robust.Shared.Configuration;
+using Robust.Shared.ContentPack;
using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Utility;
namespace Content.Client.Replay;
@@ -41,6 +49,8 @@ public sealed class ContentReplayPlaybackManager
[Dependency] private readonly IClientAdminManager _adminMan = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IBaseClient _client = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IResourceManager _resMan = default!;
///
/// UI state to return to when stopping a replay or loading fails.
@@ -50,6 +60,13 @@ public sealed class ContentReplayPlaybackManager
public bool IsScreenshotMode = false;
private bool _initialized;
+
+ ///
+ /// Most recently loaded file, for re-attempting the load with error tolerance.
+ /// Required because the zip reader auto-disposes and I'm too lazy to change it so that
+ /// can re-open it.
+ ///
+ public (ResPath? Zip, ResPath Folder)? LastLoad;
public void Initialize()
{
@@ -73,11 +90,50 @@ private void LoadOverride(IReplayFileReader fileReader)
private void OnFinishedLoading(Exception? exception)
{
- if (exception != null)
+ if (exception == null)
+ {
+ LastLoad = null;
+ return;
+ }
+
+ ReturnToDefaultState();
+
+ // Show a popup window with the error message
+ var text = Loc.GetString("replay-loading-failed", ("reason", exception));
+ var box = new BoxContainer
+ {
+ Orientation = BoxContainer.LayoutOrientation.Vertical,
+ Children = {new Label {Text = text}}
+ };
+
+ var popup = new DefaultWindow { Title = "Error!" };
+ popup.Contents.AddChild(box);
+
+ // Add button for attempting to re-load the replay while ignoring some errors.
+ if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is {} last)
{
- ReturnToDefaultState();
- _uiMan.Popup(Loc.GetString("replay-loading-failed", ("reason", exception)));
+ var button = new Button
+ {
+ Text = Loc.GetString("replay-loading-retry"),
+ StyleClasses = { StyleBase.ButtonCaution }
+ };
+
+ button.OnPressed += _ =>
+ {
+ _cfg.SetCVar(CVars.ReplayIgnoreErrors, true);
+ popup.Dispose();
+
+ IReplayFileReader reader = last.Zip == null
+ ? new ReplayFileReaderResources(_resMan, last.Folder)
+ : new ReplayFileReaderZip(new(_resMan.UserData.OpenRead(last.Zip.Value)), last.Folder);
+
+ _loadMan.LoadAndStartReplay(reader);
+ };
+
+ box.AddChild(button);
}
+
+ popup.OpenCentered();
}
public void ReturnToDefaultState()
diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
index 86d113defb1..2fa862f3df7 100644
--- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
+++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
@@ -3,7 +3,7 @@
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Events;
-using Content.Shared.Physics.Pull;
+using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Throwing;
namespace Content.Client.Replay.Spectator;
diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs
index 6e7d0d2a1bd..49d29d8a5f4 100644
--- a/Content.Client/Revenant/RevenantSystem.cs
+++ b/Content.Client/Revenant/RevenantSystem.cs
@@ -1,3 +1,5 @@
+using Content.Client.Alerts;
+using Content.Shared.Alert;
using Content.Shared.Revenant;
using Content.Shared.Revenant.Components;
using Robust.Client.GameObjects;
@@ -13,6 +15,7 @@ public override void Initialize()
base.Initialize();
SubscribeLocalEvent(OnAppearanceChange);
+ SubscribeLocalEvent(OnUpdateAlert);
}
private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args)
@@ -36,4 +39,16 @@ private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref
args.Sprite.LayerSetState(0, component.State);
}
}
+
+ private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args)
+ {
+ if (args.Alert.AlertType != AlertType.Essence)
+ return;
+
+ var sprite = args.SpriteViewEnt.Comp;
+ var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999);
+ sprite.LayerSetState(RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}");
+ sprite.LayerSetState(RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}");
+ sprite.LayerSetState(RevenantVisualLayers.Digit3, $"{essence % 10}");
+ }
}
diff --git a/Content.Client/Sandbox/SandboxSystem.cs b/Content.Client/Sandbox/SandboxSystem.cs
index d16751e3715..6a1129bb75d 100644
--- a/Content.Client/Sandbox/SandboxSystem.cs
+++ b/Content.Client/Sandbox/SandboxSystem.cs
@@ -16,6 +16,7 @@ public sealed class SandboxSystem : SharedSandboxSystem
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IPlacementManager _placement = default!;
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
private bool _sandboxEnabled;
public bool SandboxAllowed { get; private set; }
@@ -108,7 +109,7 @@ public bool Copy(ICommonSession? session, EntityCoordinates coords, EntityUid ui
}
// Try copy tile.
- if (!_map.TryFindGridAt(coords.ToMap(EntityManager), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef))
+ if (!_map.TryFindGridAt(coords.ToMap(EntityManager, _transform), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef))
return false;
if (_placement.Eraser)
diff --git a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs
index 035823af430..284c6681905 100644
--- a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs
@@ -66,11 +66,11 @@ public BaseShuttleControl(float minRange, float maxRange, float range) : base(mi
protected void DrawData(DrawingHandleScreen handle, string text)
{
- var coordsDimensions = handle.GetDimensions(Font, text, UIScale);
+ var coordsDimensions = handle.GetDimensions(Font, text, 1f);
const float coordsMargins = 5f;
handle.DrawString(Font,
- new Vector2(coordsMargins, Height) - new Vector2(0f, coordsDimensions.Y + coordsMargins),
+ new Vector2(coordsMargins, PixelHeight) - new Vector2(0f, coordsDimensions.Y + coordsMargins),
text,
Color.FromSrgb(IFFComponent.SelfColor));
}
@@ -88,7 +88,6 @@ protected void DrawCircles(DrawingHandleScreen handle)
var cornerDistance = MathF.Sqrt(WorldRange * WorldRange + WorldRange * WorldRange);
var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y));
- var distOffset = -24f;
for (var radius = minDistance; radius <= maxDistance; radius *= EquatorialMultiplier)
{
diff --git a/Content.Client/Shuttles/UI/MapScreen.xaml.cs b/Content.Client/Shuttles/UI/MapScreen.xaml.cs
index 8430699bae1..10800b8c5f7 100644
--- a/Content.Client/Shuttles/UI/MapScreen.xaml.cs
+++ b/Content.Client/Shuttles/UI/MapScreen.xaml.cs
@@ -5,6 +5,7 @@
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Content.Shared.Shuttles.UI.MapObjects;
+using Content.Shared.Timing;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
@@ -38,16 +39,11 @@ public sealed partial class MapScreen : BoxContainer
private EntityUid? _shuttleEntity;
private FTLState _state;
- private float _ftlDuration;
+ private StartEndTime _ftlTime;
private List _beacons = new();
private List _exclusions = new();
- ///
- /// When the next FTL state change happens.
- ///
- private TimeSpan _nextFtlTime;
-
private TimeSpan _nextPing;
private TimeSpan _pingCooldown = TimeSpan.FromSeconds(3);
private TimeSpan _nextMapDequeue;
@@ -114,8 +110,7 @@ public void UpdateState(ShuttleMapInterfaceState state)
_beacons = state.Destinations;
_exclusions = state.Exclusions;
_state = state.FTLState;
- _ftlDuration = state.FTLDuration;
- _nextFtlTime = _timing.CurTime + TimeSpan.FromSeconds(_ftlDuration);
+ _ftlTime = state.FTLTime;
MapRadar.InFtl = true;
MapFTLState.Text = Loc.GetString($"shuttle-console-ftl-state-{_state.ToString()}");
@@ -268,9 +263,10 @@ private void RebuildMapObjects()
while (mapComps.MoveNext(out var mapComp, out var mapXform, out var mapMetadata))
{
- if (!_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId))
- continue;
-
+ if (_console != null && !_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId, _console.Value))
+ {
+ continue;
+ }
var mapName = mapMetadata.EntityName;
if (string.IsNullOrEmpty(mapName))
@@ -315,7 +311,6 @@ private void RebuildMapObjects()
};
_mapHeadings.Add(mapComp.MapId, gridContents);
-
foreach (var grid in _mapManager.GetAllMapGrids(mapComp.MapId))
{
_entManager.TryGetComponent(grid.Owner, out IFFComponent? iffComp);
@@ -332,8 +327,8 @@ private void RebuildMapObjects()
{
AddMapObject(mapComp.MapId, gridObj);
}
- else if (iffComp == null ||
- (iffComp.Flags & IFFFlags.Hide) == 0x0)
+ else if (!_shuttles.IsBeaconMap(_mapManager.GetMapEntityId(mapComp.MapId)) && (iffComp == null ||
+ (iffComp.Flags & IFFFlags.Hide) == 0x0))
{
_pendingMapObjects.Add((mapComp.MapId, gridObj));
}
@@ -511,20 +506,8 @@ protected override void FrameUpdate(FrameEventArgs args)
MapRebuildButton.Disabled = false;
}
- var ftlDiff = (float) (_nextFtlTime - _timing.CurTime).TotalSeconds;
-
- float ftlRatio;
-
- if (_ftlDuration.Equals(0f))
- {
- ftlRatio = 1f;
- }
- else
- {
- ftlRatio = Math.Clamp(1f - (ftlDiff / _ftlDuration), 0f, 1f);
- }
-
- FTLBar.Value = ftlRatio;
+ var progress = _ftlTime.ProgressAt(curTime);
+ FTLBar.Value = float.IsFinite(progress) ? progress : 1;
}
protected override void Draw(DrawingHandleScreen handle)
diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
index 2ce1906d3d2..2f35a8dffd7 100644
--- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
@@ -116,7 +116,7 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args)
var mapTransform = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
- if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePosition, PixelRect, out var foundBeacon, out _))
+ if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePixelPosition, PixelRect, out var foundBeacon, out _))
{
RequestBeaconFTL?.Invoke(foundBeacon.Entity, _ftlAngle);
}
@@ -206,7 +206,8 @@ private void DrawParallax(DrawingHandleScreen handle)
private List GetViewportMapObjects(Matrix3 matty, List mapObjects)
{
var results = new List();
- var viewBox = SizeBox.Scale(1.2f);
+ var enlargement = new Vector2i((int) (16 * UIScale), (int) (16 * UIScale));
+ var viewBox = new UIBox2i(Vector2i.Zero - enlargement, PixelSize + enlargement);
foreach (var mapObj in mapObjects)
{
@@ -398,8 +399,8 @@ protected override void Draw(DrawingHandleScreen handle)
foreach (var (gridUiPos, iffText) in sendStrings)
{
- var textWidth = handle.GetDimensions(_font, iffText, UIScale);
- handle.DrawString(_font, gridUiPos + textWidth with { X = -textWidth.X / 2f }, iffText, adjustedColor);
+ var textWidth = handle.GetDimensions(_font, iffText, 1f);
+ handle.DrawString(_font, gridUiPos + textWidth with { X = -textWidth.X / 2f, Y = textWidth.Y * UIScale }, iffText, adjustedColor);
}
}
@@ -587,7 +588,7 @@ private bool TryGetBeacon(IEnumerable mapObjects, Matrix3 mapTransfo
var distance = (localPos - mousePos).Length();
- if (distance > BeaconSnapRange ||
+ if (distance > BeaconSnapRange * UIScale ||
distance > nearestValue)
{
continue;
diff --git a/Content.Client/StatusIcon/StatusIconOverlay.cs b/Content.Client/StatusIcon/StatusIconOverlay.cs
index 1cfb4c2a558..56107cbc025 100644
--- a/Content.Client/StatusIcon/StatusIconOverlay.cs
+++ b/Content.Client/StatusIcon/StatusIconOverlay.cs
@@ -3,9 +3,9 @@
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
-using System.Numerics;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
+using System.Numerics;
namespace Content.Client.StatusIcon;
@@ -18,7 +18,7 @@ public sealed class StatusIconOverlay : Overlay
private readonly SpriteSystem _sprite;
private readonly TransformSystem _transform;
private readonly StatusIconSystem _statusIcon;
- private readonly ShaderInstance _shader;
+ private readonly ShaderInstance _unshadedShader;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
@@ -29,7 +29,7 @@ internal StatusIconOverlay()
_sprite = _entity.System();
_transform = _entity.System();
_statusIcon = _entity.System();
- _shader = _prototype.Index("unshaded").Instance();
+ _unshadedShader = _prototype.Index("unshaded").Instance();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -42,8 +42,6 @@ protected override void Draw(in OverlayDrawArgs args)
var scaleMatrix = Matrix3.CreateScale(new Vector2(1, 1));
var rotationMatrix = Matrix3.CreateRotation(-eyeRot);
- handle.UseShader(_shader);
-
var query = _entity.AllEntityQueryEnumerator();
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
{
@@ -93,7 +91,7 @@ protected override void Draw(in OverlayDrawArgs args)
accOffsetL += texture.Height;
countL++;
}
- yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetL / EyeManager.PixelsPerMeter;
+ yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetL - proto.Offset) / EyeManager.PixelsPerMeter;
xOffset = -(bounds.Width + sprite.Offset.X) / 2f;
}
@@ -106,16 +104,21 @@ protected override void Draw(in OverlayDrawArgs args)
accOffsetR += texture.Height;
countR++;
}
- yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetR / EyeManager.PixelsPerMeter;
+ yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetR - proto.Offset) / EyeManager.PixelsPerMeter;
xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float) texture.Width / EyeManager.PixelsPerMeter;
}
+ if (proto.IsShaded)
+ handle.UseShader(null);
+ else
+ handle.UseShader(_unshadedShader);
+
var position = new Vector2(xOffset, yOffset);
handle.DrawTexture(texture, position);
}
- }
- handle.UseShader(null);
+ handle.UseShader(null);
+ }
}
}
diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
index b549918d7c4..f87b92bc615 100644
--- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs
+++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
@@ -1,22 +1,27 @@
using Content.Shared.Store;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
using System.Linq;
-using System.Threading;
-using Serilog;
-using Timer = Robust.Shared.Timing.Timer;
+using Robust.Shared.Prototypes;
namespace Content.Client.Store.Ui;
[UsedImplicitly]
public sealed class StoreBoundUserInterface : BoundUserInterface
{
+ private IPrototypeManager _prototypeManager = default!;
+
[ViewVariables]
private StoreMenu? _menu;
[ViewVariables]
private string _windowName = Loc.GetString("store-ui-default-title");
+ [ViewVariables]
+ private string _search = "";
+
+ [ViewVariables]
+ private HashSet _listings = new();
+
public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -49,6 +54,12 @@ protected override void Open()
SendMessage(new StoreRequestUpdateInterfaceMessage());
};
+ _menu.SearchTextUpdated += (_, search) =>
+ {
+ _search = search.Trim().ToLowerInvariant();
+ UpdateListingsWithSearchFilter();
+ };
+
_menu.OnRefundAttempt += (_) =>
{
SendMessage(new StoreRequestRefundMessage());
@@ -64,10 +75,10 @@ protected override void UpdateState(BoundUserInterfaceState state)
switch (state)
{
case StoreUpdateState msg:
- _menu.UpdateBalance(msg.Balance);
- _menu.PopulateStoreCategoryButtons(msg.Listings);
+ _listings = msg.Listings;
- _menu.UpdateListing(msg.Listings.ToList());
+ _menu.UpdateBalance(msg.Balance);
+ UpdateListingsWithSearchFilter();
_menu.SetFooterVisibility(msg.ShowFooter);
_menu.UpdateRefund(msg.AllowRefund);
break;
@@ -89,4 +100,19 @@ protected override void Dispose(bool disposing)
_menu?.Close();
_menu?.Dispose();
}
+
+ private void UpdateListingsWithSearchFilter()
+ {
+ if (_menu == null)
+ return;
+
+ var filteredListings = new HashSet(_listings);
+ if (!string.IsNullOrEmpty(_search))
+ {
+ filteredListings.RemoveWhere(listingData => !ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listingData, _prototypeManager).Trim().ToLowerInvariant().Contains(_search) &&
+ !ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(listingData, _prototypeManager).Trim().ToLowerInvariant().Contains(_search));
+ }
+ _menu.PopulateStoreCategoryButtons(filteredListings);
+ _menu.UpdateListing(filteredListings.ToList());
+ }
}
diff --git a/Content.Client/Store/Ui/StoreMenu.xaml b/Content.Client/Store/Ui/StoreMenu.xaml
index 4b38352a44a..fc4cbe444fc 100644
--- a/Content.Client/Store/Ui/StoreMenu.xaml
+++ b/Content.Client/Store/Ui/StoreMenu.xaml
@@ -28,7 +28,8 @@
HorizontalAlignment="Right"
Text="Refund" />
-
+
+
diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs
index 5dc1ab246bd..67e5d360a3a 100644
--- a/Content.Client/Store/Ui/StoreMenu.xaml.cs
+++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs
@@ -1,5 +1,4 @@
using System.Linq;
-using System.Threading;
using Content.Client.Actions;
using Content.Client.GameTicking.Managers;
using Content.Client.Message;
@@ -27,6 +26,7 @@ public sealed partial class StoreMenu : DefaultWindow
private StoreWithdrawWindow? _withdrawWindow;
+ public event EventHandler? SearchTextUpdated;
public event Action? OnListingButtonPressed;
public event Action? OnCategoryButtonPressed;
public event Action? OnWithdrawAttempt;
@@ -46,6 +46,7 @@ public StoreMenu(string name)
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
RefreshButton.OnButtonDown += OnRefreshButtonDown;
RefundButton.OnButtonDown += OnRefundButtonDown;
+ SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text);
if (Window != null)
Window.Title = name;
@@ -59,7 +60,7 @@ public void UpdateBalance(Dictionary balance)
(type.Key, type.Value), type => _prototypeManager.Index(type.Key));
var balanceStr = string.Empty;
- foreach (var ((type, amount),proto) in currency)
+ foreach (var ((_, amount), proto) in currency)
{
balanceStr += Loc.GetString("store-ui-balance-display", ("amount", amount),
("currency", Loc.GetString(proto.DisplayName, ("amount", 1))));
@@ -81,7 +82,6 @@ public void UpdateListing(List listings)
{
var sorted = listings.OrderBy(l => l.Priority).ThenBy(l => l.Cost.Values.Sum());
-
// should probably chunk these out instead. to-do if this clogs the internet tubes.
// maybe read clients prototypes instead?
ClearListings();
@@ -129,8 +129,8 @@ private void AddListingGui(ListingData listing)
if (!listing.Categories.Contains(CurrentCategory))
return;
- var listingName = Loc.GetString(listing.Name);
- var listingDesc = Loc.GetString(listing.Description);
+ var listingName = ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager);
+ var listingDesc = ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(listing, _prototypeManager);
var listingPrice = listing.Cost;
var canBuy = CanBuyListing(Balance, listingPrice);
@@ -144,12 +144,6 @@ private void AddListingGui(ListingData listing)
{
if (texture == null)
texture = spriteSys.GetPrototypeIcon(listing.ProductEntity).Default;
-
- var proto = _prototypeManager.Index(listing.ProductEntity);
- if (listingName == string.Empty)
- listingName = proto.Name;
- if (listingDesc == string.Empty)
- listingDesc = proto.Description;
}
else if (listing.ProductAction != null)
{
@@ -243,13 +237,16 @@ public void PopulateStoreCategoryButtons(HashSet listings)
allCategories = allCategories.OrderBy(c => c.Priority).ToList();
+ // This will reset the Current Category selection if nothing matches the search.
+ if (allCategories.All(category => category.ID != CurrentCategory))
+ CurrentCategory = string.Empty;
+
if (CurrentCategory == string.Empty && allCategories.Count > 0)
CurrentCategory = allCategories.First().ID;
- if (allCategories.Count <= 1)
- return;
-
CategoryListContainer.Children.Clear();
+ if (allCategories.Count < 1)
+ return;
foreach (var proto in allCategories)
{
diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs
index 13ba259dbcd..a10e3eb5926 100644
--- a/Content.Client/Stylesheets/StyleNano.cs
+++ b/Content.Client/Stylesheets/StyleNano.cs
@@ -45,6 +45,7 @@ public sealed class StyleNano : StyleBase
public const string StyleClassBorderedWindowPanel = "BorderedWindowPanel";
public const string StyleClassInventorySlotBackground = "InventorySlotBackground";
public const string StyleClassHandSlotHighlight = "HandSlotHighlight";
+ public const string StyleClassChatPanel = "ChatPanel";
public const string StyleClassChatSubPanel = "ChatSubPanel";
public const string StyleClassTransparentBorderedWindowPanel = "TransparentBorderedWindowPanel";
public const string StyleClassHotbarPanel = "HotbarPanel";
@@ -144,6 +145,8 @@ public sealed class StyleNano : StyleBase
public const string StyleClassButtonColorRed = "ButtonColorRed";
public const string StyleClassButtonColorGreen = "ButtonColorGreen";
+ public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD");
+
public override Stylesheet Stylesheet { get; }
public StyleNano(IResourceCache resCache) : base(resCache)
@@ -290,7 +293,7 @@ public StyleNano(IResourceCache resCache) : base(resCache)
var buttonTex = resCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
var topButtonBase = new StyleBoxTexture
{
- Texture = buttonTex,
+ Texture = buttonTex,
};
topButtonBase.SetPatchMargin(StyleBox.Margin.All, 10);
topButtonBase.SetPadding(StyleBox.Margin.All, 0);
@@ -298,19 +301,19 @@ public StyleNano(IResourceCache resCache) : base(resCache)
var topButtonOpenRight = new StyleBoxTexture(topButtonBase)
{
- Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(0, 0), new Vector2(14, 24))),
+ Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(0, 0), new Vector2(14, 24))),
};
topButtonOpenRight.SetPatchMargin(StyleBox.Margin.Right, 0);
var topButtonOpenLeft = new StyleBoxTexture(topButtonBase)
{
- Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(14, 24))),
+ Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(14, 24))),
};
topButtonOpenLeft.SetPatchMargin(StyleBox.Margin.Left, 0);
var topButtonSquare = new StyleBoxTexture(topButtonBase)
{
- Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(3, 24))),
+ Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(3, 24))),
};
topButtonSquare.SetPatchMargin(StyleBox.Margin.Horizontal, 0);
@@ -346,12 +349,16 @@ public StyleNano(IResourceCache resCache) : base(resCache)
lineEdit.SetPatchMargin(StyleBox.Margin.All, 3);
lineEdit.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
- var chatSubBGTex = resCache.GetTexture("/Textures/Interface/Nano/chat_sub_background.png");
- var chatSubBG = new StyleBoxTexture
+ var chatBg = new StyleBoxFlat
+ {
+ BackgroundColor = ChatBackgroundColor
+ };
+
+ var chatSubBg = new StyleBoxFlat
{
- Texture = chatSubBGTex,
+ BackgroundColor = ChatBackgroundColor,
};
- chatSubBG.SetPatchMargin(StyleBox.Margin.All, 2);
+ chatSubBg.SetContentMarginOverride(StyleBox.Margin.All, 2);
var actionSearchBoxTex = resCache.GetTexture("/Textures/Interface/Nano/black_panel_dark_thin_border.png");
var actionSearchBox = new StyleBoxTexture
@@ -368,9 +375,9 @@ public StyleNano(IResourceCache resCache) : base(resCache)
};
tabContainerPanel.SetPatchMargin(StyleBox.Margin.All, 2);
- var tabContainerBoxActive = new StyleBoxFlat {BackgroundColor = new Color(64, 64, 64)};
+ var tabContainerBoxActive = new StyleBoxFlat { BackgroundColor = new Color(64, 64, 64) };
tabContainerBoxActive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
- var tabContainerBoxInactive = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 32)};
+ var tabContainerBoxInactive = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 32) };
tabContainerBoxInactive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
var progressBarBackground = new StyleBoxFlat
@@ -409,21 +416,21 @@ public StyleNano(IResourceCache resCache) : base(resCache)
// Placeholder
var placeholderTexture = resCache.GetTexture("/Textures/Interface/Nano/placeholder.png");
- var placeholder = new StyleBoxTexture {Texture = placeholderTexture};
+ var placeholder = new StyleBoxTexture { Texture = placeholderTexture };
placeholder.SetPatchMargin(StyleBox.Margin.All, 19);
placeholder.SetExpandMargin(StyleBox.Margin.All, -5);
placeholder.Mode = StyleBoxTexture.StretchMode.Tile;
- var itemListBackgroundSelected = new StyleBoxFlat {BackgroundColor = new Color(75, 75, 75)};
+ var itemListBackgroundSelected = new StyleBoxFlat { BackgroundColor = new Color(75, 75, 75) };
itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
- var itemListItemBackgroundDisabled = new StyleBoxFlat {BackgroundColor = new Color(10, 10, 10)};
+ var itemListItemBackgroundDisabled = new StyleBoxFlat { BackgroundColor = new Color(10, 10, 10) };
itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
- var itemListItemBackground = new StyleBoxFlat {BackgroundColor = new Color(55, 55, 55)};
+ var itemListItemBackground = new StyleBoxFlat { BackgroundColor = new Color(55, 55, 55) };
itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
- var itemListItemBackgroundTransparent = new StyleBoxFlat {BackgroundColor = Color.Transparent};
+ var itemListItemBackgroundTransparent = new StyleBoxFlat { BackgroundColor = Color.Transparent };
itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
@@ -489,9 +496,9 @@ public StyleNano(IResourceCache resCache) : base(resCache)
sliderForeBox.SetPatchMargin(StyleBox.Margin.All, 12);
sliderGrabBox.SetPatchMargin(StyleBox.Margin.All, 12);
- var sliderFillGreen = new StyleBoxTexture(sliderFillBox) {Modulate = Color.LimeGreen};
- var sliderFillRed = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Red};
- var sliderFillBlue = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Blue};
+ var sliderFillGreen = new StyleBoxTexture(sliderFillBox) { Modulate = Color.LimeGreen };
+ var sliderFillRed = new StyleBoxTexture(sliderFillBox) { Modulate = Color.Red };
+ var sliderFillBlue = new StyleBoxTexture(sliderFillBox) { Modulate = Color.Blue };
var sliderFillWhite = new StyleBoxTexture(sliderFillBox) { Modulate = Color.White };
var boxFont13 = resCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
@@ -850,19 +857,19 @@ public StyleNano(IResourceCache resCache) : base(resCache)
Element().Pseudo(TextEdit.StylePseudoClassPlaceholder)
.Prop("font-color", Color.Gray),
- // Chat lineedit - we don't actually draw a stylebox around the lineedit itself, we put it around the
- // input + other buttons, so we must clear the default stylebox
- new StyleRule(new SelectorElement(typeof(LineEdit), new[] {StyleClassChatLineEdit}, null, null),
+ // chat subpanels (chat lineedit backing, popup backings)
+ new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {StyleClassChatPanel}, null, null),
new[]
{
- new StyleProperty(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()),
+ new StyleProperty(PanelContainer.StylePropertyPanel, chatBg),
}),
- // chat subpanels (chat lineedit backing, popup backings)
- new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {StyleClassChatSubPanel}, null, null),
+ // Chat lineedit - we don't actually draw a stylebox around the lineedit itself, we put it around the
+ // input + other buttons, so we must clear the default stylebox
+ new StyleRule(new SelectorElement(typeof(LineEdit), new[] {StyleClassChatLineEdit}, null, null),
new[]
{
- new StyleProperty(PanelContainer.StylePropertyPanel, chatSubBG),
+ new StyleProperty(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()),
}),
// Action searchbox lineedit
@@ -1468,6 +1475,25 @@ public StyleNano(IResourceCache resCache) : base(resCache)
Element
public void SetSeverity(short? severity)
{
- if (_severity != severity)
- {
- _severity = severity;
- _icon.SetFromSpriteSpecifier(Alert.GetIcon(_severity));
- }
+ if (_severity == severity)
+ return;
+ _severity = severity;
+
+ if (!_entityManager.TryGetComponent(_spriteViewEntity, out var sprite))
+ return;
+ var icon = Alert.GetIcon(_severity);
+ if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer))
+ sprite.LayerSetSprite(layer, icon);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
+ UserInterfaceManager.GetUIController().UpdateAlertSpriteEntity(_spriteViewEntity, Alert);
+
if (!Cooldown.HasValue)
{
_cooldownGraphic.Visible = false;
@@ -91,5 +113,17 @@ protected override void FrameUpdate(FrameEventArgs args)
_cooldownGraphic.FromTime(Cooldown.Value.Start, Cooldown.Value.End);
}
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ _entityManager.DeleteEntity(_spriteViewEntity);
+ }
+ }
+
+ public enum AlertVisualLayers : byte
+ {
+ Base
}
}
diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
index 0ad78f974e5..fd4e742cf7e 100644
--- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
+++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
@@ -9,6 +9,7 @@
using Content.Client.Examine;
using Content.Client.Gameplay;
using Content.Client.Ghost;
+using Content.Client.Stylesheets;
using Content.Client.UserInterface.Screens;
using Content.Client.UserInterface.Systems.Chat.Widgets;
using Content.Client.UserInterface.Systems.Gameplay;
@@ -55,7 +56,6 @@ public sealed class ChatUIController : UIController
[Dependency] private readonly IStateManager _state = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
- [Dependency] private readonly IConfigurationManager _cfg = default!;
[UISystemDependency] private readonly ExamineSystem? _examine = default;
[UISystemDependency] private readonly GhostSystem? _ghost = default;
@@ -184,8 +184,8 @@ public override void Initialize()
_net.RegisterNetMessage(OnChatMessage);
_net.RegisterNetMessage(OnDeleteChatMessagesBy);
SubscribeNetworkEvent(OnDamageForceSay);
- _cfg.OnValueChanged(CCVars.ChatEnableColorName, (value) => { _chatNameColorsEnabled = value; });
- _chatNameColorsEnabled = _cfg.GetCVar(CCVars.ChatEnableColorName);
+ _config.OnValueChanged(CCVars.ChatEnableColorName, (value) => { _chatNameColorsEnabled = value; });
+ _chatNameColorsEnabled = _config.GetCVar(CCVars.ChatEnableColorName);
_speechBubbleRoot = new LayoutContainer();
@@ -237,6 +237,9 @@ public override void Initialize()
{
_chatNameColors[i] = nameColors[i].ToHex();
}
+
+ _config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged);
+
}
public void OnScreenLoad()
@@ -245,6 +248,8 @@ public void OnScreenLoad()
var viewportContainer = UIManager.ActiveScreen!.FindControl("ViewportContainer");
SetSpeechBubbleRoot(viewportContainer);
+
+ SetChatWindowOpacity(_config.GetCVar(CCVars.ChatWindowOpacity));
}
public void OnScreenUnload()
@@ -252,6 +257,34 @@ public void OnScreenUnload()
SetMainChat(false);
}
+ private void OnChatWindowOpacityChanged(float opacity)
+ {
+ SetChatWindowOpacity(opacity);
+ }
+
+ private void SetChatWindowOpacity(float opacity)
+ {
+ var chatBox = UIManager.ActiveScreen?.GetWidget() ?? UIManager.ActiveScreen?.GetWidget();
+
+ var panel = chatBox?.ChatWindowPanel;
+ if (panel is null)
+ return;
+
+ Color color;
+ if (panel.PanelOverride is StyleBoxFlat styleBoxFlat)
+ color = styleBoxFlat.BackgroundColor;
+ else if (panel.TryGetStyleProperty(PanelContainer.StylePropertyPanel, out var style)
+ && style is StyleBoxFlat propStyleBoxFlat)
+ color = propStyleBoxFlat.BackgroundColor;
+ else
+ color = StyleNano.ChatBackgroundColor;
+
+ panel.PanelOverride = new StyleBoxFlat
+ {
+ BackgroundColor = color.WithAlpha(opacity)
+ };
+ }
+
public void SetMainChat(bool setting)
{
if (UIManager.ActiveScreen == null)
@@ -784,7 +817,7 @@ private void OnChatMessage(MsgChatMessage message)
ProcessChatMessage(msg);
if ((msg.Channel & ChatChannel.AdminRelated) == 0 ||
- _cfg.GetCVar(CCVars.ReplayRecordAdminChat))
+ _config.GetCVar(CCVars.ReplayRecordAdminChat))
{
_replayRecording.RecordClientMessage(msg);
}
@@ -844,7 +877,7 @@ public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
break;
case ChatChannel.LOOC:
- if (_cfg.GetCVar(CCVars.LoocAboveHeadShow))
+ if (_config.GetCVar(CCVars.LoocAboveHeadShow))
AddSpeechBubble(msg, SpeechBubble.SpeechType.Looc);
break;
}
diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs
similarity index 95%
rename from Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs
rename to Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs
index 843fd46c1a0..0326664bd69 100644
--- a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs
+++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs
@@ -1,4 +1,5 @@
-using Content.Shared.Chat;
+using Content.Client.Stylesheets;
+using Content.Shared.Chat;
using Content.Shared.Input;
using Robust.Client.UserInterface.Controls;
@@ -44,6 +45,7 @@ public ChatInputBox()
StyleClasses = {"chatFilterOptionButton"}
};
Container.AddChild(FilterButton);
+ AddStyleClass(StyleNano.StyleClassChatSubPanel);
ChannelSelector.OnChannelSelect += UpdateActiveChannel;
}
diff --git a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml
index 090041fa93c..36cdce85985 100644
--- a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml
+++ b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml
@@ -7,11 +7,8 @@
HorizontalExpand="True"
VerticalExpand="True"
MinSize="465 225">
-
-
-
-
-
+
diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml
index 2c622b82b9e..d469e6ced03 100644
--- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml
+++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml
@@ -1,4 +1,4 @@
-
-
+
diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs
index bd952fe9577..e39ac5d322d 100644
--- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs
+++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs
@@ -12,6 +12,7 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Storage.Controls;
@@ -355,6 +356,40 @@ protected override void FrameUpdate(FrameEventArgs args)
origin,
currentLocation.Rotation);
+ foreach (var locations in storageComponent.SavedLocations)
+ {
+ if (!_entity.TryGetComponent(currentEnt, out var meta) || meta.EntityName != locations.Key)
+ continue;
+
+ float spot = 0;
+ var marked = new List();
+
+ foreach (var location in locations.Value)
+ {
+ var shape = itemSystem.GetAdjustedItemShape(currentEnt, location);
+ var bound = shape.GetBoundingBox();
+
+ var spotFree = storageSystem.ItemFitsInGridLocation(currentEnt, StorageEntity.Value, location);
+
+ if (spotFree)
+ spot++;
+
+ for (var y = bound.Bottom; y <= bound.Top; y++)
+ {
+ for (var x = bound.Left; x <= bound.Right; x++)
+ {
+ if (TryGetBackgroundCell(x, y, out var cell) && shape.Contains(x, y) && !marked.Contains(cell))
+ {
+ marked.Add(cell);
+ cell.ModulateSelfOverride = spotFree
+ ? Color.FromHsv((0.18f, 1 / spot, 0.5f / spot + 0.5f, 1f))
+ : Color.FromHex("#2222CC");
+ }
+ }
+ }
+ }
+ }
+
var validColor = usingInHand ? Color.Goldenrod : Color.FromHex("#1E8000");
for (var y = itemBounding.Bottom; y <= itemBounding.Top; y++)
diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
index 5f3ae3a2dac..b865b54dd0e 100644
--- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
+++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
@@ -240,6 +240,16 @@ private void OnPiecePressed(GUIBoundKeyEventArgs args, ItemGridPiece control)
args.Handle();
}
+ else if (args.Function == ContentKeyFunctions.SaveItemLocation)
+ {
+ if (_container?.StorageEntity is not {} storage)
+ return;
+
+ _entity.RaisePredictiveEvent(new StorageSaveItemLocationEvent(
+ _entity.GetNetEntity(control.Entity),
+ _entity.GetNetEntity(storage)));
+ args.Handle();
+ }
else if (args.Function == ContentKeyFunctions.ExamineEntity)
{
_entity.System().DoExamine(control.Entity);
diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
index 0650482b108..f700c6663b9 100644
--- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
+++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
@@ -1,10 +1,13 @@
using Content.Shared.VoiceMask;
using Robust.Client.GameObjects;
+using Robust.Shared.Prototypes;
namespace Content.Client.VoiceMask;
public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
{
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+
[ViewVariables]
private VoiceMaskNameChangeWindow? _window;
@@ -16,10 +19,11 @@ protected override void Open()
{
base.Open();
- _window = new();
+ _window = new(_proto);
_window.OpenCentered();
_window.OnNameChange += OnNameSelected;
+ _window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
_window.OnClose += Close;
}
@@ -35,7 +39,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
}
- _window.UpdateState(cast.Name);
+ _window.UpdateState(cast.Name, cast.Verb);
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
index 2316ec9c7d8..e23aca12391 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
@@ -1,11 +1,16 @@
-
-
+ MinSize="5 30">
+
-
+
+
+
+
+
-
+
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
index e373acbd0a0..16a28f9d9b3 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
@@ -1,26 +1,85 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Speech;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
namespace Content.Client.VoiceMask;
[GenerateTypedNameReferences]
-public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
+public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
{
public Action? OnNameChange;
+ public Action? OnVerbChange;
- public VoiceMaskNameChangeWindow()
+ private List<(string, string)> _verbs = new();
+
+ private string? _verb;
+
+ public VoiceMaskNameChangeWindow(IPrototypeManager proto)
{
RobustXamlLoader.Load(this);
NameSelectorSet.OnPressed += _ =>
{
- OnNameChange!(NameSelector.Text);
+ OnNameChange?.Invoke(NameSelector.Text);
+ };
+
+ SpeechVerbSelector.OnItemSelected += args =>
+ {
+ OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
+ SpeechVerbSelector.SelectId(args.Id);
};
+
+ ReloadVerbs(proto);
+
+ AddVerbs();
+ }
+
+ private void ReloadVerbs(IPrototypeManager proto)
+ {
+ foreach (var verb in proto.EnumeratePrototypes())
+ {
+ _verbs.Add((Loc.GetString(verb.Name), verb.ID));
+ }
+ _verbs.Sort((a, b) => a.Item1.CompareTo(b.Item1));
}
- public void UpdateState(string name)
+ private void AddVerbs()
+ {
+ SpeechVerbSelector.Clear();
+
+ AddVerb(Loc.GetString("chat-speech-verb-name-none"), null);
+ foreach (var (name, id) in _verbs)
+ {
+ AddVerb(name, id);
+ }
+ }
+
+ private void AddVerb(string name, string? verb)
+ {
+ var id = SpeechVerbSelector.ItemCount;
+ SpeechVerbSelector.AddItem(name);
+ if (verb is {} metadata)
+ SpeechVerbSelector.SetItemMetadata(id, metadata);
+
+ if (verb == _verb)
+ SpeechVerbSelector.SelectId(id);
+ }
+
+ public void UpdateState(string name, string? verb)
{
NameSelector.Text = name;
+ _verb = verb;
+
+ for (int id = 0; id < SpeechVerbSelector.ItemCount; id++)
+ {
+ if (string.Equals(verb, SpeechVerbSelector.GetItemMetadata(id)))
+ {
+ SpeechVerbSelector.SelectId(id);
+ break;
+ }
+ }
}
}
diff --git a/Content.Client/Wall/ShuttleWallVisuals.cs b/Content.Client/Wall/ShuttleWallVisuals.cs
new file mode 100644
index 00000000000..9060e892a8a
--- /dev/null
+++ b/Content.Client/Wall/ShuttleWallVisuals.cs
@@ -0,0 +1,6 @@
+namespace Content.Client.Wall;
+
+public enum ShuttleWallVisualLayers : byte
+{
+ Deconstruction,
+}
diff --git a/Content.Client/Weather/WeatherSystem.cs b/Content.Client/Weather/WeatherSystem.cs
index 24de0bc8c4c..b35483bba48 100644
--- a/Content.Client/Weather/WeatherSystem.cs
+++ b/Content.Client/Weather/WeatherSystem.cs
@@ -124,9 +124,9 @@ protected override void Run(EntityUid uid, WeatherData weather, WeatherPrototype
comp.Occlusion = occlusion;
}
- protected override bool SetState(WeatherState state, WeatherComponent comp, WeatherData weather, WeatherPrototype weatherProto)
+ protected override bool SetState(EntityUid uid, WeatherState state, WeatherComponent comp, WeatherData weather, WeatherPrototype weatherProto)
{
- if (!base.SetState(state, comp, weather, weatherProto))
+ if (!base.SetState(uid, state, comp, weather, weatherProto))
return false;
if (!Timing.IsFirstTimePredicted)
@@ -164,7 +164,7 @@ private void OnWeatherHandleState(EntityUid uid, WeatherComponent component, ref
continue;
// New weather
- StartWeather(component, ProtoMan.Index(proto), weather.EndTime);
+ StartWeather(uid, component, ProtoMan.Index(proto), weather.EndTime);
}
}
}
diff --git a/Content.Client/Zombies/ZombieSystem.cs b/Content.Client/Zombies/ZombieSystem.cs
index bd89e978c70..49b5d6aec18 100644
--- a/Content.Client/Zombies/ZombieSystem.cs
+++ b/Content.Client/Zombies/ZombieSystem.cs
@@ -15,6 +15,7 @@ public override void Initialize()
SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnCanDisplayStatusIcons);
+ SubscribeLocalEvent(OnCanDisplayStatusIcons);
}
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
@@ -36,7 +37,18 @@ private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartu
///
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
{
- if (HasComp(args.User) || HasComp(args.User))
+ if (HasComp(args.User) || HasComp(args.User) || HasComp(args.User))
+ return;
+
+ if (component.IconVisibleToGhost && HasComp(args.User))
+ return;
+
+ args.Cancelled = true;
+ }
+
+ private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args)
+ {
+ if (HasComp(args.User) && !HasComp(args.User))
return;
if (component.IconVisibleToGhost && HasComp(args.User))
diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs
index 6046ed65e44..b544fe28547 100644
--- a/Content.IntegrationTests/PoolManager.cs
+++ b/Content.IntegrationTests/PoolManager.cs
@@ -306,11 +306,6 @@ await testOut.WriteLineAsync(
Pairs[fallback!] = true;
}
- if (fallback == null && _pairId > 8)
- {
- var x = 2;
- }
-
return fallback;
}
}
diff --git a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs
index 7d86fc2f6c2..895bb2cf878 100644
--- a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs
+++ b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs
@@ -57,9 +57,9 @@ await server.WaitAssertion(() =>
var reader = new AccessReaderComponent();
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "Bar" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "Bar" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True);
});
// test deny
@@ -67,58 +67,58 @@ await server.WaitAssertion(() =>
reader.DenyTags.Add("A");
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A", "Foo" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A", "Foo" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True);
});
// test one list
reader = new AccessReaderComponent();
- reader.AccessLists.Add(new HashSet { "A" });
+ reader.AccessLists.Add(new HashSet> { "A" });
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False);
});
// test one list - two items
reader = new AccessReaderComponent();
- reader.AccessLists.Add(new HashSet { "A", "B" });
+ reader.AccessLists.Add(new HashSet> { "A", "B" });
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False);
});
// test two list
reader = new AccessReaderComponent();
- reader.AccessLists.Add(new HashSet { "A" });
- reader.AccessLists.Add(new HashSet { "B", "C" });
+ reader.AccessLists.Add(new HashSet> { "A" });
+ reader.AccessLists.Add(new HashSet> { "B", "C" });
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False);
});
// test deny list
reader = new AccessReaderComponent();
- reader.AccessLists.Add(new HashSet { "A" });
+ reader.AccessLists.Add(new HashSet> { "A" });
reader.DenyTags.Add("B");
Assert.Multiple(() =>
{
- Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
- Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.False);
- Assert.That(system.AreAccessTagsAllowed(Array.Empty(), reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False);
+ Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False);
});
});
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs
index d0325480acd..dce3741c98d 100644
--- a/Content.IntegrationTests/Tests/Body/LungTest.cs
+++ b/Content.IntegrationTests/Tests/Body/LungTest.cs
@@ -9,7 +9,6 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
-using Robust.Shared.Maths;
using System.Linq;
using System.Numerics;
@@ -61,12 +60,11 @@ public async Task AirConsistencyTest()
var mapManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
var mapLoader = entityManager.System();
- RespiratorSystem respSys = default;
- MetabolizerSystem metaSys = default;
MapId mapId;
EntityUid? grid = null;
BodyComponent body = default;
+ RespiratorComponent resp = default;
EntityUid human = default;
GridAtmosphereComponent relevantAtmos = default;
var startingMoles = 0.0f;
@@ -99,17 +97,15 @@ float GetMapMoles()
await server.WaitAssertion(() =>
{
- var coords = new Vector2(0.5f, -1f);
- var coordinates = new EntityCoordinates(grid.Value, coords);
+ var center = new Vector2(0.5f, 0.5f);
+ var coordinates = new EntityCoordinates(grid.Value, center);
human = entityManager.SpawnEntity("HumanLungDummy", coordinates);
- respSys = entityManager.System();
- metaSys = entityManager.System();
relevantAtmos = entityManager.GetComponent(grid.Value);
- startingMoles = GetMapMoles();
+ startingMoles = 100f; // Hardcoded because GetMapMoles returns 900 here for some reason.
#pragma warning disable NUnit2045
Assert.That(entityManager.TryGetComponent(human, out body), Is.True);
- Assert.That(entityManager.HasComponent(human), Is.True);
+ Assert.That(entityManager.TryGetComponent(human, out resp), Is.True);
#pragma warning restore NUnit2045
});
@@ -118,18 +114,19 @@ await server.WaitAssertion(() =>
var inhaleCycles = 100;
for (var i = 0; i < inhaleCycles; i++)
{
- await server.WaitAssertion(() =>
- {
- // inhale
- respSys.Update(2.0f);
- Assert.That(GetMapMoles(), Is.LessThan(startingMoles));
-
- // metabolize + exhale
- metaSys.Update(1.0f);
- metaSys.Update(1.0f);
- respSys.Update(2.0f);
- Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0001));
- });
+ // Breathe in
+ await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Exhaling);
+ Assert.That(
+ GetMapMoles(), Is.LessThan(startingMoles),
+ "Did not inhale in any gas"
+ );
+
+ // Breathe out
+ await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Inhaling);
+ Assert.That(
+ GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002),
+ "Did not exhale as much gas as was inhaled"
+ );
}
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
index 6e2a080370d..7c700d9fb8a 100644
--- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
+++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
@@ -181,9 +181,8 @@ await server.WaitAssertion(() =>
#pragma warning restore NUnit2045
// Move away from the chair
- var xformQuery = entityManager.GetEntityQuery();
- var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
- xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000), xformQuery);
+ var oldWorldPosition = xformSystem.GetWorldPosition(chair);
+ xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000));
// Out of range
#pragma warning disable NUnit2045 // Interdependent asserts.
@@ -193,8 +192,8 @@ await server.WaitAssertion(() =>
#pragma warning restore NUnit2045
// Move near the chair
- oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
- xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0), xformQuery);
+ oldWorldPosition = xformSystem.GetWorldPosition(chair);
+ xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0));
// In range
#pragma warning disable NUnit2045 // Interdependent asserts.
@@ -220,8 +219,8 @@ await server.WaitAssertion(() =>
Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle));
// Move away from the chair
- oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
- xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0), xformQuery);
+ oldWorldPosition = xformSystem.GetWorldPosition(chair);
+ xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0));
});
await server.WaitRunTicks(1);
@@ -371,9 +370,8 @@ await server.WaitAssertion(() =>
});
// Move the buckled entity away
- var xformQuery = entityManager.GetEntityQuery();
- var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
- xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0), xformQuery);
+ var oldWorldPosition = xformSystem.GetWorldPosition(chair);
+ xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0));
});
await PoolManager.WaitUntil(server, () => !buckle.Buckled, 10);
@@ -383,9 +381,8 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() =>
{
// Move the now unbuckled entity back onto the chair
- var xformQuery = entityManager.GetEntityQuery();
- var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
- xformSystem.SetWorldPosition(human, oldWorldPosition, xformQuery);
+ var oldWorldPosition = xformSystem.GetWorldPosition(chair);
+ xformSystem.SetWorldPosition(human, oldWorldPosition);
// Buckle
Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle));
diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs
index 976fc2eceb5..9109fdbe4f7 100644
--- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs
+++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs
@@ -163,7 +163,6 @@ public async Task Test()
var entityManager = server.ResolveDependency();
var xformSystem = entityManager.System();
var disposalSystem = entityManager.System();
-
await server.WaitAssertion(() =>
{
// Spawn the entities
@@ -171,8 +170,7 @@ await server.WaitAssertion(() =>
human = entityManager.SpawnEntity("HumanDisposalDummy", coordinates);
wrench = entityManager.SpawnEntity("WrenchDummy", coordinates);
disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates);
- disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy",
- entityManager.GetComponent(disposalUnit).MapPosition);
+ disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", coordinates);
// Test for components existing
unitUid = disposalUnit;
@@ -204,10 +202,10 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() =>
{
- // Move the disposal trunk away
- var xform = entityManager.GetComponent(disposalTrunk);
var worldPos = xformSystem.GetWorldPosition(disposalTrunk);
- xformSystem.SetWorldPosition(xform, worldPos + new Vector2(1, 0));
+
+ // Move the disposal trunk away
+ xformSystem.SetWorldPosition(disposalTrunk, worldPos + new Vector2(1, 0));
// Fail to flush with a mob and an item
Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench);
@@ -215,10 +213,12 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() =>
{
- // Move the disposal trunk back
var xform = entityManager.GetComponent(disposalTrunk);
- var worldPos = xformSystem.GetWorldPosition(disposalTrunk);
- xformSystem.SetWorldPosition(xform, worldPos - new Vector2(1, 0));
+ var worldPos = xformSystem.GetWorldPosition(disposalUnit);
+
+ // Move the disposal trunk back
+ xformSystem.SetWorldPosition(disposalTrunk, worldPos);
+ xformSystem.AnchorEntity((disposalTrunk, xform));
// Fail to flush with a mob and an item, no power
Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench);
@@ -240,6 +240,7 @@ await server.WaitAssertion(() =>
// Re-pressurizing
Flush(disposalUnit, unitComponent, false, disposalSystem);
});
+
await pair.CleanReturnAsync();
}
}
diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
index 0d852bf2f0e..6e88d6928e6 100644
--- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
+++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs
@@ -72,7 +72,7 @@ await server.WaitPost(() =>
var puddleOrigin = new Vector2i(0, 0);
await server.WaitAssertion(() =>
{
- var grid = mapManager.GetGrid(gridId);
+ var grid = entityManager.GetComponent(gridId);
var solution = new Solution("Blood", FixedPoint2.New(100));
var tileRef = grid.GetTileRef(puddleOrigin);
#pragma warning disable NUnit2045 // Interdependent tests
@@ -86,7 +86,7 @@ await server.WaitAssertion(() =>
await server.WaitAssertion(() =>
{
- var grid = mapManager.GetGrid(gridId);
+ var grid = entityManager.GetComponent(gridId);
var puddle = GetPuddleEntity(entityManager, grid, puddleOrigin);
#pragma warning disable NUnit2045 // Interdependent tests
diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
index 3213bba51f9..611af673809 100644
--- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
+++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs
@@ -67,7 +67,7 @@ await server.WaitPost(() =>
await server.WaitAssertion(() =>
{
- var coordinates = grid.ToCoordinates();
+ var coordinates = grid.Owner.ToCoordinates();
var solution = new Solution("Water", FixedPoint2.New(20));
Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False);
diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
index 1d5dd6d34e2..c6a8e618cc1 100644
--- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
+++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
@@ -58,7 +58,6 @@ await server.WaitAssertion(() =>
var cuffableSys = entityManager.System();
var xformSys = entityManager.System();
- var xformQuery = entityManager.GetEntityQuery();
// Spawn the entities
human = entityManager.SpawnEntity("HumanHandcuffDummy", coordinates);
@@ -66,8 +65,8 @@ await server.WaitAssertion(() =>
cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
- var coords = xformSys.GetWorldPosition(otherHuman, xformQuery);
- xformSys.SetWorldPosition(human, coords, xformQuery);
+ var coords = xformSys.GetWorldPosition(otherHuman);
+ xformSys.SetWorldPosition(human, coords);
// Test for components existing
Assert.Multiple(() =>
diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
index 84e1afaf45c..88448e7b800 100644
--- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
+++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
@@ -1006,15 +1006,10 @@ protected async Task AddAtmosphere(EntityUid? uid = null)
await Server.WaitPost(() =>
{
var atmosSystem = SEntMan.System();
- var atmos = SEntMan.EnsureComponent(target);
var moles = new float[Atmospherics.AdjustedNumberOfGases];
moles[(int) Gas.Oxygen] = 21.824779f;
moles[(int) Gas.Nitrogen] = 82.10312f;
- atmosSystem.SetMapAtmosphere(target, false, new GasMixture(2500)
- {
- Temperature = 293.15f,
- Moles = moles,
- }, atmos);
+ atmosSystem.SetMapAtmosphere(target, false, new GasMixture(moles, Atmospherics.T20C));
});
}
diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs
index ba5ecaf12d1..07a6ddd89f1 100644
--- a/Content.IntegrationTests/Tests/PostMapInitTest.cs
+++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs
@@ -57,6 +57,7 @@ public sealed class PostMapInitTest
"Asterisk", //DeltaV
"TheHive", //DeltaV
"Hammurabi", //DeltaV
+ "Lighthouse", //DeltaV
"Submarine" //DeltaV
};
diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs
index d4e2cde9b0d..a6af3e6a65b 100644
--- a/Content.IntegrationTests/Tests/Power/PowerTest.cs
+++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs
@@ -176,16 +176,18 @@ await server.WaitAssertion(() =>
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
+
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
+ var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
+ var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
supplier = entityManager.GetComponent(generatorEnt);
consumer1 = entityManager.GetComponent(consumerEnt1);
@@ -237,16 +239,18 @@ await server.WaitAssertion(() =>
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
+
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
+ var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
+ var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
supplier = entityManager.GetComponent(generatorEnt);
consumer1 = entityManager.GetComponent(consumerEnt1);
@@ -292,16 +296,17 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
supplier = entityManager.GetComponent(generatorEnt);
consumer = entityManager.GetComponent(consumerEnt);
@@ -383,16 +388,17 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates());
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
netBattery = entityManager.GetComponent(generatorEnt);
battery = entityManager.GetComponent(generatorEnt);
@@ -486,17 +492,18 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
- var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
+ var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates(0, 2));
netBattery = entityManager.GetComponent(batteryEnt);
battery = entityManager.GetComponent(batteryEnt);
supplier = entityManager.GetComponent(generatorEnt);
@@ -577,16 +584,17 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 3; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
- var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
+ var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", gridOwner.ToCoordinates(0, 2));
supplier = entityManager.GetComponent(generatorEnt);
var netBattery = entityManager.GetComponent(batteryEnt);
@@ -635,20 +643,21 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 4; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
+ var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
+ var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
consumer = entityManager.GetComponent(consumerEnt);
supplier = entityManager.GetComponent(supplyEnt);
@@ -712,20 +721,21 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 4; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
+ var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
+ var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
consumer = entityManager.GetComponent(consumerEnt);
supplier = entityManager.GetComponent(supplyEnt);
@@ -787,6 +797,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Map layout here is
// C - consumer
@@ -800,18 +811,18 @@ await server.WaitAssertion(() =>
for (var i = 0; i < 5; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
+ entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2));
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4));
+ var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1));
+ var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3));
+ var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2));
+ var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
+ var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4));
consumer1 = entityManager.GetComponent(consumerEnt1);
consumer2 = entityManager.GetComponent(consumerEnt2);
@@ -888,6 +899,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Layout is two generators, two batteries, and one load. As to why two: because previously this test
// would fail ONLY if there were more than two batteries present, because each of them tries to supply
@@ -900,16 +912,16 @@ await server.WaitAssertion(() =>
for (var i = -2; i <= 2; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, -2));
+ var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
+ var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, -2));
- var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 1));
- var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, -1));
+ var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 1));
+ var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, -1));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
consumer = entityManager.GetComponent(consumerEnt);
supplier1 = entityManager.GetComponent(supplyEnt1);
@@ -981,6 +993,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Map layout here is
// C - consumer
@@ -994,18 +1007,18 @@ await server.WaitAssertion(() =>
for (var i = 0; i < 5; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
+ entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2));
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4));
+ var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1));
+ var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3));
+ var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2));
+ var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
+ var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4));
consumer1 = entityManager.GetComponent(consumerEnt1);
consumer2 = entityManager.GetComponent(consumerEnt2);
@@ -1068,20 +1081,21 @@ await server.WaitPost(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 4; i++)
{
grid.SetTile(new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
}
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
+ var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
+ var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
+ var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
consumer = entityManager.GetComponent(consumerEnt);
supplier = entityManager.GetComponent(supplyEnt);
@@ -1153,6 +1167,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 4; i++)
@@ -1160,15 +1175,15 @@ await server.WaitAssertion(() =>
grid.SetTile(new Vector2i(0, i), new Tile(1));
}
- var leftEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 2));
- var rightEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 3));
+ var leftEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 2));
+ var rightEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 3));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
+ var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180);
- var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
+ var battery = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
var batteryNodeContainer = entityManager.GetComponent(battery);
if (nodeContainer.TryGetNode(entityManager.GetComponent(leftEnt),
@@ -1216,6 +1231,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
// Power only works when anchored
for (var i = 0; i < 3; i++)
@@ -1223,14 +1239,14 @@ await server.WaitAssertion(() =>
grid.SetTile(new Vector2i(0, i), new Tile(1));
}
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0));
- entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 2));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0));
+ entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1));
+ entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 1));
+ entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 2));
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
- var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.ToCoordinates(0, 1));
- var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 2));
+ var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
+ var substationEnt = entityManager.SpawnEntity("SubstationDummy", gridOwner.ToCoordinates(0, 1));
+ var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 2));
var generatorSupplier = entityManager.GetComponent(generatorEnt);
substationNetBattery = entityManager.GetComponent(substationEnt);
@@ -1273,6 +1289,7 @@ await server.WaitAssertion(() =>
{
var map = mapManager.CreateMap();
var grid = mapManager.CreateGrid(map);
+ var gridOwner = grid.Owner;
const int range = 5;
@@ -1282,15 +1299,15 @@ await server.WaitAssertion(() =>
grid.SetTile(new Vector2i(0, i), new Tile(1));
}
- var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 0));
- var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0));
+ var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 0));
+ var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", gridOwner.ToCoordinates(0, 0));
// Create a powered receiver in range (range is 0 indexed)
- var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range - 1));
+ var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range - 1));
receiver = entityManager.GetComponent(powerReceiverEnt);
// Create an unpowered receiver outside range
- var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range));
+ var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range));
unpoweredReceiver = entityManager.GetComponent(unpoweredReceiverEnt);
var battery = entityManager.GetComponent(apcEnt);
diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs
index 6096c497efa..e4a9c1a840d 100644
--- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs
+++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs
@@ -59,7 +59,7 @@ await server.WaitPost(() =>
var tileDefinition = tileDefinitionManager["FloorSteel"]; // Wires n such disable ambiance while under the floor
var tile = new Tile(tileDefinition.TileId);
- var coordinates = grid.ToCoordinates();
+ var coordinates = grid.Owner.ToCoordinates();
grid.SetTile(coordinates, tile);
});
@@ -94,7 +94,7 @@ await server.WaitPost(() =>
await server.WaitAssertion(() =>
{
Assert.That(!mapManager.IsMapInitialized(mapId));
- var testLocation = grid.ToCoordinates();
+ var testLocation = grid.Owner.ToCoordinates();
Assert.Multiple(() =>
{
diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs
index ba91f54ff77..87d174f7272 100644
--- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs
+++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs
@@ -1,6 +1,6 @@
using Content.Shared.Hands.Components;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Prototypes;
-using Content.Shared.Pulling.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
@@ -29,7 +29,7 @@ await server.WaitAssertion(() =>
{
foreach (var proto in protoManager.EnumeratePrototypes())
{
- if (!proto.TryGetComponent(out SharedPullerComponent? puller))
+ if (!proto.TryGetComponent(out PullerComponent? puller))
continue;
if (!puller.NeedsHands)
diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs
index ed3c484b435..cbcdd1c6c62 100644
--- a/Content.IntegrationTests/Tests/Tag/TagTest.cs
+++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs
@@ -130,9 +130,9 @@ await server.WaitAssertion(() =>
Assert.Multiple(() =>
{
// Cannot add the starting tag again
- Assert.That(tagSystem.AddTag(sTagComponent, StartingTag), Is.False);
- Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, StartingTag), Is.False);
- Assert.That(tagSystem.AddTags(sTagComponent, new List { StartingTag, StartingTag }), Is.False);
+ Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False);
+ Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False);
+ Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, StartingTag }), Is.False);
// Has the starting tag
Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True);
@@ -157,22 +157,22 @@ await server.WaitAssertion(() =>
Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False);
// Cannot remove a tag that does not exist
- Assert.That(tagSystem.RemoveTag(sTagComponent, AddedTag), Is.False);
- Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.False);
- Assert.That(tagSystem.RemoveTags(sTagComponent, new List { AddedTag, AddedTag }), Is.False);
+ Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False);
+ Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False);
+ Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { AddedTag, AddedTag }), Is.False);
});
// Can add the new tag
- Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.True);
+ Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True);
Assert.Multiple(() =>
{
// Cannot add it twice
- Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.False);
+ Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False);
// Cannot add existing tags
- Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, AddedTag), Is.False);
- Assert.That(tagSystem.AddTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False);
+ Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False);
+ Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False);
// Now has two tags
Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2));
@@ -191,16 +191,16 @@ await server.WaitAssertion(() =>
Assert.Multiple(() =>
{
// Remove the existing starting tag
- Assert.That(tagSystem.RemoveTag(sTagComponent, StartingTag), Is.True);
+ Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True);
// Remove the existing added tag
- Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.True);
+ Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True);
});
Assert.Multiple(() =>
{
// No tags left to remove
- Assert.That(tagSystem.RemoveTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False);
+ Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False);
// No tags left in the component
Assert.That(sTagComponent.Tags, Is.Empty);
diff --git a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs
new file mode 100644
index 00000000000..920dc088186
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs
@@ -0,0 +1,103 @@
+using Content.Server.Doors;
+using Content.Server.Power;
+using Content.Server.Wires;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Map;
+
+namespace Content.IntegrationTests.Tests.Wires;
+
+[TestFixture]
+[Parallelizable(ParallelScope.All)]
+[TestOf(typeof(WiresSystem))]
+public sealed class WireLayoutTest
+{
+ [TestPrototypes]
+ public const string Prototypes = """
+ - type: wireLayout
+ id: WireLayoutTest
+ dummyWires: 2
+ wires:
+ - !type:PowerWireAction
+ - !type:DoorBoltWireAction
+
+ - type: wireLayout
+ id: WireLayoutTest2
+ parent: WireLayoutTest
+ wires:
+ - !type:PowerWireAction
+
+ - type: wireLayout
+ id: WireLayoutTest3
+ parent: WireLayoutTest
+
+ - type: entity
+ id: WireLayoutTest
+ components:
+ - type: Wires
+ layoutId: WireLayoutTest
+
+ - type: entity
+ id: WireLayoutTest2
+ components:
+ - type: Wires
+ layoutId: WireLayoutTest2
+
+ - type: entity
+ id: WireLayoutTest3
+ components:
+ - type: Wires
+ layoutId: WireLayoutTest3
+ """;
+
+ [Test]
+ public async Task TestLayoutInheritance()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var server = pair.Server;
+ var testMap = await pair.CreateTestMap();
+
+ await server.WaitAssertion(() =>
+ {
+ var wires = IoCManager.Resolve().GetEntitySystem();
+
+ // Need to spawn these entities to make sure the wire layouts are initialized.
+ var ent1 = SpawnWithComp(server.EntMan, "WireLayoutTest", testMap.MapCoords);
+ var ent2 = SpawnWithComp(server.EntMan, "WireLayoutTest2", testMap.MapCoords);
+ var ent3 = SpawnWithComp(server.EntMan, "WireLayoutTest3", testMap.MapCoords);
+
+ // Assert.That(wires.TryGetLayout("WireLayoutTest", out var layout1));
+ // Assert.That(wires.TryGetLayout("WireLayoutTest2", out var layout2));
+ // Assert.That(wires.TryGetLayout("WireLayoutTest3", out var layout3));
+
+ Assert.Multiple(() =>
+ {
+ // Entity 1.
+ Assert.That(ent1.Comp.WiresList, Has.Count.EqualTo(4));
+ Assert.That(ent1.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires");
+ Assert.That(ent1.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 power wire");
+ Assert.That(ent1.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire");
+
+ Assert.That(ent2.Comp.WiresList, Has.Count.EqualTo(5));
+ Assert.That(ent2.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires");
+ Assert.That(ent2.Comp.WiresList, Has.Exactly(2).With.Property("Action").InstanceOf(), "2 power wire");
+ Assert.That(ent2.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire");
+
+ Assert.That(ent3.Comp.WiresList, Has.Count.EqualTo(4));
+ Assert.That(ent3.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires");
+ Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 power wire");
+ Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire");
+ });
+ });
+
+ await pair.CleanReturnAsync();
+ }
+
+ private static Entity SpawnWithComp(IEntityManager entityManager, string prototype, MapCoordinates coords)
+ where T : IComponent, new()
+ {
+ var ent = entityManager.Spawn(prototype, coords);
+ var comp = entityManager.EnsureComponent(ent);
+ return new Entity(ent, comp);
+ }
+}
diff --git a/Content.Packaging/ClientPackaging.cs b/Content.Packaging/ClientPackaging.cs
index a989ebd968e..a66d4ec5b91 100644
--- a/Content.Packaging/ClientPackaging.cs
+++ b/Content.Packaging/ClientPackaging.cs
@@ -13,7 +13,7 @@ public static class ClientPackaging
///
/// Be advised this can be called from server packaging during a HybridACZ build.
///
- public static async Task PackageClient(bool skipBuild, IPackageLogger logger)
+ public static async Task PackageClient(bool skipBuild, string configuration, IPackageLogger logger)
{
logger.Info("Building client...");
@@ -26,7 +26,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
{
"build",
Path.Combine("Content.Client", "Content.Client.csproj"),
- "-c", "Release",
+ "-c", configuration,
"--nologo",
"/v:m",
"/t:Rebuild",
diff --git a/Content.Packaging/CommandLineArgs.cs b/Content.Packaging/CommandLineArgs.cs
index 9f2b0755357..23f661921e0 100644
--- a/Content.Packaging/CommandLineArgs.cs
+++ b/Content.Packaging/CommandLineArgs.cs
@@ -31,6 +31,11 @@ public sealed class CommandLineArgs
///
public bool HybridAcz { get; set; }
+ ///
+ /// Configuration used for when packaging the server. (Release, Debug, Tools)
+ ///
+ public string Configuration { get; set; }
+
// CommandLineArgs, 3rd of her name.
public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out CommandLineArgs? parsed)
{
@@ -39,6 +44,7 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out
var skipBuild = false;
var wipeRelease = true;
var hybridAcz = false;
+ var configuration = "Release";
List? platforms = null;
using var enumerator = args.GetEnumerator();
@@ -89,6 +95,16 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out
platforms ??= new List();
platforms.Add(enumerator.Current);
}
+ else if (arg == "--configuration")
+ {
+ if (!enumerator.MoveNext())
+ {
+ Console.WriteLine("No configuration provided");
+ return false;
+ }
+
+ configuration = enumerator.Current;
+ }
else if (arg == "--help")
{
PrintHelp();
@@ -106,7 +122,7 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out
return false;
}
- parsed = new CommandLineArgs(client.Value, skipBuild, wipeRelease, hybridAcz, platforms);
+ parsed = new CommandLineArgs(client.Value, skipBuild, wipeRelease, hybridAcz, platforms, configuration);
return true;
}
@@ -120,6 +136,7 @@ private static void PrintHelp()
--no-wipe-release Don't wipe the release folder before creating files.
--hybrid-acz Use HybridACZ for server builds.
--platform Platform for server builds. Default will output several x64 targets.
+ --configuration Configuration to use for building the server (Release, Debug, Tools). Default is Release.
");
}
@@ -128,12 +145,14 @@ private CommandLineArgs(
bool skipBuild,
bool wipeRelease,
bool hybridAcz,
- List? platforms)
+ List? platforms,
+ string configuration)
{
Client = client;
SkipBuild = skipBuild;
WipeRelease = wipeRelease;
HybridAcz = hybridAcz;
Platforms = platforms;
+ Configuration = configuration;
}
}
diff --git a/Content.Packaging/Program.cs b/Content.Packaging/Program.cs
index ba5924ec3e9..65c0e0131a4 100644
--- a/Content.Packaging/Program.cs
+++ b/Content.Packaging/Program.cs
@@ -17,11 +17,11 @@
if (parsed.Client)
{
- await ClientPackaging.PackageClient(parsed.SkipBuild, logger);
+ await ClientPackaging.PackageClient(parsed.SkipBuild, parsed.Configuration, logger);
}
else
{
- await ServerPackaging.PackageServer(parsed.SkipBuild, parsed.HybridAcz, logger, parsed.Platforms);
+ await ServerPackaging.PackageServer(parsed.SkipBuild, parsed.HybridAcz, logger, parsed.Configuration, parsed.Platforms);
}
void WipeBin()
diff --git a/Content.Packaging/ServerPackaging.cs b/Content.Packaging/ServerPackaging.cs
index ba489629f79..d9ca57c4d11 100644
--- a/Content.Packaging/ServerPackaging.cs
+++ b/Content.Packaging/ServerPackaging.cs
@@ -69,7 +69,7 @@ public static class ServerPackaging
"zh-Hant"
};
- public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, List? platforms = null)
+ public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, string configuration, List? platforms = null)
{
if (platforms == null)
{
@@ -82,7 +82,7 @@ public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageL
// Rather than hosting the client ZIP on the watchdog or on a separate server,
// Hybrid ACZ uses the ACZ hosting functionality to host it as part of the status host,
// which means that features such as automatic UPnP forwarding still work properly.
- await ClientPackaging.PackageClient(skipBuild, logger);
+ await ClientPackaging.PackageClient(skipBuild, configuration, logger);
}
// Good variable naming right here.
@@ -91,13 +91,13 @@ public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageL
if (!platforms.Contains(platform.Rid))
continue;
- await BuildPlatform(platform, skipBuild, hybridAcz, logger);
+ await BuildPlatform(platform, skipBuild, hybridAcz, configuration, logger);
}
}
- private static async Task BuildPlatform(PlatformReg platform, bool skipBuild, bool hybridAcz, IPackageLogger logger)
+ private static async Task BuildPlatform(PlatformReg platform, bool skipBuild, bool hybridAcz, string configuration, IPackageLogger logger)
{
- logger.Info($"Building project for {platform}...");
+ logger.Info($"Building project for {platform.TargetOs}...");
if (!skipBuild)
{
@@ -108,7 +108,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
{
"build",
Path.Combine("Content.Server", "Content.Server.csproj"),
- "-c", "Release",
+ "-c", configuration,
"--nologo",
"/v:m",
$"/p:TargetOs={platform.TargetOs}",
@@ -118,7 +118,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
}
});
- await PublishClientServer(platform.Rid, platform.TargetOs);
+ await PublishClientServer(platform.Rid, platform.TargetOs, configuration);
}
logger.Info($"Packaging {platform.Rid} server...");
@@ -137,7 +137,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
logger.Info($"Finished packaging server in {sw.Elapsed}");
}
- private static async Task PublishClientServer(string runtime, string targetOs)
+ private static async Task PublishClientServer(string runtime, string targetOs, string configuration)
{
await ProcessHelpers.RunCheck(new ProcessStartInfo
{
@@ -147,7 +147,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
"publish",
"--runtime", runtime,
"--no-self-contained",
- "-c", "Release",
+ "-c", configuration,
$"/p:TargetOs={targetOs}",
"/p:FullRelease=True",
"/m",
diff --git a/Content.Replay/Menu/ReplayMainMenu.cs b/Content.Replay/Menu/ReplayMainMenu.cs
index 8bd99f82fb2..85c39c59dac 100644
--- a/Content.Replay/Menu/ReplayMainMenu.cs
+++ b/Content.Replay/Menu/ReplayMainMenu.cs
@@ -1,6 +1,7 @@
using System.IO.Compression;
using System.Linq;
using Content.Client.Message;
+using Content.Client.Replay;
using Content.Client.UserInterface.Systems.EscapeMenu;
using Robust.Client;
using Robust.Client.Replays.Loading;
@@ -31,6 +32,7 @@ public sealed class ReplayMainScreen : State
[Dependency] private readonly IGameController _controllerProxy = default!;
[Dependency] private readonly IClientRobustSerializer _serializer = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!;
private ReplayMainMenuControl _mainMenuControl = default!;
private SelectReplayWindow? _selectWindow;
@@ -207,12 +209,13 @@ private void OnFolderPressed(BaseButton.ButtonEventArgs obj)
private void OnLoadPressed(BaseButton.ButtonEventArgs obj)
{
- if (_selected.HasValue)
- {
- var fileReader = new ReplayFileReaderZip(
- new ZipArchive(_resMan.UserData.OpenRead(_selected.Value)), ReplayZipFolder);
- _loadMan.LoadAndStartReplay(fileReader);
- }
+ if (!_selected.HasValue)
+ return;
+
+ _replayMan.LastLoad = (_selected.Value, ReplayZipFolder);
+ var fileReader = new ReplayFileReaderZip(
+ new ZipArchive(_resMan.UserData.OpenRead(_selected.Value)), ReplayZipFolder);
+ _loadMan.LoadAndStartReplay(fileReader);
}
private void RefreshReplays()
diff --git a/Content.Server.Database/Migrations/Postgres/20240318022005_AdminMessageDismiss.Designer.cs b/Content.Server.Database/Migrations/Postgres/20240318022005_AdminMessageDismiss.Designer.cs
new file mode 100644
index 00000000000..1a47a51b943
--- /dev/null
+++ b/Content.Server.Database/Migrations/Postgres/20240318022005_AdminMessageDismiss.Designer.cs
@@ -0,0 +1,1768 @@
+//
+using System;
+using System.Net;
+using System.Text.Json;
+using Content.Server.Database;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using NpgsqlTypes;
+
+#nullable disable
+
+namespace Content.Server.Database.Migrations.Postgres
+{
+ [DbContext(typeof(PostgresServerDbContext))]
+ [Migration("20240318022005_AdminMessageDismiss")]
+ partial class AdminMessageDismiss
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Content.Server.Database.Admin", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("AdminRankId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Title")
+ .HasColumnType("text")
+ .HasColumnName("title");
+
+ b.HasKey("UserId")
+ .HasName("PK_admin");
+
+ b.HasIndex("AdminRankId")
+ .HasDatabaseName("IX_admin_admin_rank_id");
+
+ b.ToTable("admin", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_flag_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AdminId")
+ .HasColumnType("uuid")
+ .HasColumnName("admin_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("flag");
+
+ b.Property("Negative")
+ .HasColumnType("boolean")
+ .HasColumnName("negative");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_flag");
+
+ b.HasIndex("AdminId")
+ .HasDatabaseName("IX_admin_flag_admin_id");
+
+ b.HasIndex("Flag", "AdminId")
+ .IsUnique();
+
+ b.ToTable("admin_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
+ {
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("Id")
+ .HasColumnType("integer")
+ .HasColumnName("admin_log_id");
+
+ b.Property("Date")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("date");
+
+ b.Property("Impact")
+ .HasColumnType("smallint")
+ .HasColumnName("impact");
+
+ b.Property("Json")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("json");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("message");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasColumnName("type");
+
+ b.HasKey("RoundId", "Id")
+ .HasName("PK_admin_log");
+
+ b.HasIndex("Date");
+
+ b.HasIndex("Message")
+ .HasAnnotation("Npgsql:TsVectorConfig", "english");
+
+ NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN");
+
+ b.HasIndex("Type")
+ .HasDatabaseName("IX_admin_log_type");
+
+ b.ToTable("admin_log", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
+ {
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("LogId")
+ .HasColumnType("integer")
+ .HasColumnName("log_id");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.HasKey("RoundId", "LogId", "PlayerUserId")
+ .HasName("PK_admin_log_player");
+
+ b.HasIndex("PlayerUserId")
+ .HasDatabaseName("IX_admin_log_player_player_user_id");
+
+ b.ToTable("admin_log_player", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_messages_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedById")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by_id");
+
+ b.Property("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("DeletedById")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by_id");
+
+ b.Property("Dismissed")
+ .HasColumnType("boolean")
+ .HasColumnName("dismissed");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiration_time");
+
+ b.Property("LastEditedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_edited_at");
+
+ b.Property("LastEditedById")
+ .HasColumnType("uuid")
+ .HasColumnName("last_edited_by_id");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("message");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.Property("PlaytimeAtNote")
+ .HasColumnType("interval")
+ .HasColumnName("playtime_at_note");
+
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("Seen")
+ .HasColumnType("boolean")
+ .HasColumnName("seen");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_messages");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("DeletedById");
+
+ b.HasIndex("LastEditedById");
+
+ b.HasIndex("PlayerUserId")
+ .HasDatabaseName("IX_admin_messages_player_user_id");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_messages_round_id");
+
+ b.ToTable("admin_messages", null, t =>
+ {
+ t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen");
+ });
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_notes_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedById")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by_id");
+
+ b.Property("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("DeletedById")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by_id");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiration_time");
+
+ b.Property("LastEditedAt")
+ .IsRequired()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_edited_at");
+
+ b.Property("LastEditedById")
+ .HasColumnType("uuid")
+ .HasColumnName("last_edited_by_id");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("message");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.Property("PlaytimeAtNote")
+ .HasColumnType("interval")
+ .HasColumnName("playtime_at_note");
+
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("Secret")
+ .HasColumnType("boolean")
+ .HasColumnName("secret");
+
+ b.Property("Severity")
+ .HasColumnType("integer")
+ .HasColumnName("severity");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_notes");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("DeletedById");
+
+ b.HasIndex("LastEditedById");
+
+ b.HasIndex("PlayerUserId")
+ .HasDatabaseName("IX_admin_notes_player_user_id");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_notes_round_id");
+
+ b.ToTable("admin_notes", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank");
+
+ b.ToTable("admin_rank", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_flag_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AdminRankId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("flag");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank_flag");
+
+ b.HasIndex("AdminRankId");
+
+ b.HasIndex("Flag", "AdminRankId")
+ .IsUnique();
+
+ b.ToTable("admin_rank_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_watchlists_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedById")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by_id");
+
+ b.Property("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("DeletedById")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by_id");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiration_time");
+
+ b.Property("LastEditedAt")
+ .IsRequired()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_edited_at");
+
+ b.Property("LastEditedById")
+ .HasColumnType("uuid")
+ .HasColumnName("last_edited_by_id");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("message");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.Property("PlaytimeAtNote")
+ .HasColumnType("interval")
+ .HasColumnName("playtime_at_note");
+
+ b.Property